From c2386e9e860325f43775d700a016b91ecd7053d0 Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Mon, 11 Jan 2021 08:57:15 +0100
Subject: [PATCH 01/92] Modifying auth tutorial to contain different providers.
Adding a better example repo link.
---
docs/tutorials/quickstart-app-auth.md | 265 +++++++++++++++++++++++---
1 file changed, 239 insertions(+), 26 deletions(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 1dcca9d7e0..ecafd47b8c 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -3,20 +3,17 @@ id: quickstart-app-auth
title: Monorepo App Setup With Authentication
---
-###### September 15th 2020 - @backstage/create-app - v0.1.1-alpha.21
+###### January 8th 2021 - @backstage/create-app - v0.4.5
> This document takes you through setting up a Backstage app that runs in your
> own environment. It starts with a skeleton install and verifying of the
-> monorepo's functionality. Next, GitHub authentication is added and tested.
+> monorepo's functionality. Next, authentication is added and tested.
>
> This document assumes you have Node.js 12 active along with Yarn and Python.
-> Please note, that at the time of this writing, the current version is
-> 0.1.1-alpha.21. This guide can still be used with future versions, just,
-> verify as you go. If you run into issues, you can compare your setup with mine
-> here >
-> [simple-backstage-app](https://github.com/johnson-jesse/simple-backstage-app).
+> Please note, that at the time of this writing, the current version is v0.4.5
+> This guide can still be used with future versions, just, verify as you go.
# The Skeleton Application
@@ -55,6 +52,16 @@ guest. Let's fix that now and add auth.
# The Auth Configuration
+Default Backstage installation includes multiple authentication providers out of
+the box. The steps to enable new authentication provider in Backstage are very
+similar to each other, the biggest difference is usually configuring the
+external authentication provider. Please see a subset of possible providers and
+instructions to integrate them below. Steps 1 & 2 are described separately for
+each provider and steps beyond that are common for all.
+
+Github
+
+
1. Open `app-config.yaml` and change it as follows
_from:_
@@ -75,23 +82,224 @@ auth:
$env: AUTH_GITHUB_CLIENT_ID
clientSecret:
$env: AUTH_GITHUB_CLIENT_SECRET
- ## uncomment the following three lines if using enterprise
+ ## uncomment the following two lines if using enterprise
# enterpriseInstanceUrl:
# $env: AUTH_GITHUB_ENTERPRISE_INSTANCE_URL
```
-2. Set environment variables in whatever fashion is easiest for you. I chose to
+2. Generate Github client id and secret
+
+- Log into http://github.com
+- Navigate to (Settings > Developer Settings > OAuth Apps > New OAuth
+ App)[https://github.com/settings/applications/new]
+- Set Homepage URL = http://localhost:3000
+- Set Callback URL = http://localhost:7000/api/auth/github
+- Click [Register application]
+- On the next page, copy and paste your new Client ID and Client Secret to
+ environment variables defined in the `app-config.yaml` file,
+ `AUTH_GITHUB_CLIENT_ID` & `AUTH_GITHUB_CLIENT_SECRET`
+
+
+
+
+Gitlab
+
+
+1. Open `app-config.yaml` and change it as follows
+
+_from:_
+
+```yaml
+auth:
+ providers: {}
+```
+
+_to:_
+
+```yaml
+auth:
+ providers:
+ gitlab:
+ development:
+ clientId:
+ $env: AUTH_GITLAB_CLIENT_ID
+ clientSecret:
+ $env: AUTH_GITLAB_CLIENT_SECRET
+ audience: https://gitlab.com # Or your self-hosted Gitlab instance URL
+```
+
+2. Generate Gitlab Application for client id and secret
+
+- Log into Gitlab
+- Navigate to (Profile > Settings >
+ Applications)[https://gitlab.com/-/profile/applications]
+- Name your application
+- Set Callback URL = http://localhost:7000/api/auth/gitlab/handler/frame
+- Select the following values:
+ - `read_user (Read the authenticated user's personal information)`
+ - `read_repository (Allows read-only access to the repository)`
+ - `write_repository (Allows read-write access to the repository)`
+ - `openid (Authenticate using OpenID Connect)`
+ - `profile (Allows read-only access to the user's personal information using OpenID Connect)`
+ - `email (Allows read-only access to the user's primary email address using OpenID Connect)`
+- Click [Save application]
+- On the next page, copy and paste your new Application ID and Secret to
+ environment variables defined in the `app-config.yaml` file,
+ `AUTH_GITLAB_CLIENT_ID` & `AUTH_GITLAB_CLIENT_SECRET`
+
+
+
+
+Google
+
+
+1. Open `app-config.yaml` and change it as follows
+
+_from:_
+
+```yaml
+auth:
+ providers: {}
+```
+
+_to:_
+
+```yaml
+auth:
+ providers:
+ google:
+ development:
+ clientId:
+ $env: AUTH_GOOGLE_CLIENT_ID
+ clientSecret:
+ $env: AUTH_GOOGLE_CLIENT_SECRET
+```
+
+2. Generate Google Application in Google Cloud console
+
+- Log into https://console.cloud.google.com
+- Select or create a new project from the dropdown on the top bar
+- Navigate to (APIs & Services - >
+ Credentials)[https://console.cloud.google.com/apis/credentials]
+- Add new Authorised JavaScript origin = `http://localhost:3000`
+- Add new Authorised redirect URI =
+ `http://localhost:7000/api/auth/google/handler/frame`
+- Click [Save application]
+- Google should display a modal with your Client ID and Secret. Copy and paste
+ those to environment variables defined in the `app-config.yaml` file,
+ `AUTH_GOOGLE_CLIENT_ID` & `AUTH_GOOGLE_CLIENT_SECRET`
+
+
+
+
+Microsoft
+
+
+1. Open `app-config.yaml` and change it as follows
+
+_from:_
+
+```yaml
+auth:
+ providers: {}
+```
+
+_to:_
+
+```yaml
+auth:
+ providers:
+ microsoft:
+ development:
+ clientId:
+ $env: AUTH_MICROSOFT_CLIENT_ID
+ clientSecret:
+ $env: AUTH_MICROSOFT_CLIENT_SECRET
+ tenantId:
+ $env: AUTH_MICROSOFT_TENANT_ID
+```
+
+2. Create Microsoft Directory in Microsoft Portal
+
+- Log into https://portal.azure.com
+- Navigate to (Azure Active Directory -> App
+ Registrations)[https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps]
+- Create a New Registration
+- Add new Redirect URI = `http://localhost:3000`
+- Add new Authorised redirect URI =
+ `http://localhost:7000/api/auth/microsoft/handler/frame`
+- Click [Save application]
+- Set environment variable `AUTH_MICROSOFT_CLIENT_ID` from
+ `Application (client) Id` displayed on the directory page
+- Set environment variable `AUTH_MICROSOFT_TENANT_ID` from
+ `Directory (tenant) ID` displayed on the directory page
+- Navigate to Certificates & Secrets section and click [Create a new secret]
+- Set environment variable `AUTH_MICROSOFT_CLIENT_SECRET` from the `value` field
+ created.
+
+
+
+
+Auth0
+
+
+1. Open `app-config.yaml` and change it as follows
+
+_from:_
+
+```yaml
+auth:
+ providers: {}
+```
+
+_to:_
+
+```yaml
+auth:
+ providers:
+ auth0:
+ development:
+ clientId:
+ $env: AUTH_AUTH0_CLIENT_ID
+ clientSecret:
+ $env: AUTH_AUTH0_CLIENT_SECRET
+ domain:
+ $env: AUTH_AUTH0_DOMAIN_ID
+```
+
+2. Create Auth0 application in Auth0 management console
+
+- Log into https://manage.auth0.com/dashboard/
+- Navigate to Applications
+- Create a New Application
+ - Select Single Page Web Application
+- Go to Settings tab
+- Add new line to Allowed Callback URLs =
+ `http://localhost:7000/api/auth/auth0/handler/frame`
+- Click [Save Changes]
+- Set environment variables displayed on the Basic Information page
+ - `AUTH_AUTH0_CLIENT_ID` from `Client ID` displayed on Auth0 application page
+ - `AUTH_AUTH0_CLIENT_SECRET` from `Client Secret` displayed on Auth0
+ application page
+ - `AUTH_AUTH0_DOMAIN_ID` from `Domain` displayed on Auth0 application page
+
+
+
+
+3. Set environment variables in whatever fashion is easiest for you. I chose to
add mine to my `.zshrc` profile.
```zsh
# For macOS Catalina & Z Shell
# ------ simple-backstage-app GitHub
+#
+# (Change the name of the environment variables based on your auth setup above
export AUTH_GITHUB_CLIENT_ID=xxx
export AUTH_GITHUB_CLIENT_SECRET=xxx
# export AUTH_GITHUB_ENTERPRISE_INSTANCE_URL=https://github.{MY_BIZ}.com
```
-3. And of course I need to source that file.
+4. And of course I need to source that file.
```zsh
# Loading the new variables
@@ -107,26 +315,26 @@ export AUTH_GITHUB_CLIENT_SECRET=xxx
> ...
```
-4. The values to replace `xxx` above come from your oauth app setup.
-
-```
-> Log into http://github.com
-> Navigate to (Settings > Developer Settings > OAuth Apps > New OAuth App)[https://github.com/settings/applications/new]
-> Set Homepage URL = http://localhost:3000
-> Set Callback URL = http://localhost:7000/api/auth/github
-> Click [Register application]
-> On the next page, copy and paste your new Client ID and Client Secret to the environment variables above, `AUTH_GITHUB_CLIENT_ID` & `AUTH_GITHUB_CLIENT_SECRET`
-> Don't forget to `source` that profile file again if necessary.
-```
-
-5. Open and change _root > packages > app > src >_`App.tsx` as follows
+6. Open and change _root > packages > app > src >_`App.tsx` to use correct
+ authentication provider reference
```tsx
-// Add the following imports to the existing list from core
import { githubAuthApiRef, SignInPage } from '@backstage/core';
```
-6. In the same file, change the createApp function as follows
+Modify the imported reference based on authentication method selected above
+
+| Auth Provider | Import Name |
+| ------------- | ------------------- |
+| Github | githubAuthApiRef |
+| Gitlab | gitlabAuthApiRef |
+| Google | googleAuthApiRef |
+| Microsoft | microsoftAuthApiRef |
+| Auth0 | googleAuthApiRef |
+
+7. In the same file, modify createApp
+
+Remeber to modify the provider information based on the table above.
```tsx
const app = createApp({
@@ -153,12 +361,17 @@ const app = createApp({
});
```
-7. Start the backend and frontend as before
+After finishing setting up one (or multiple) authentication providers defined
+above you can start the backend and frontend as before
When the browser loads, you should be presented with a login page for GitHub.
Login as usual with your GitHub account. If this is your first time, you will be
asked to authorize and then are redirected to the catalog page if all is well.
+For more information you can clone the repository:
+https://github.com/RoadieHQ/backstage-auth-example Each authentication setting
+is set up there on a branch named after the authentication provider.
+
# Where to go from here
> You're probably eager to write your first custom plugin. Follow this next
From e9c75d64a3aff49109d8f5851d17c0f7d02455df Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Mon, 11 Jan 2021 09:02:26 +0100
Subject: [PATCH 02/92] Fix typos.
---
docs/tutorials/quickstart-app-auth.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index ecafd47b8c..5991f626e7 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -293,7 +293,7 @@ auth:
# For macOS Catalina & Z Shell
# ------ simple-backstage-app GitHub
#
-# (Change the name of the environment variables based on your auth setup above
+# (Change the name of the environment variables based on your auth setup above)
export AUTH_GITHUB_CLIENT_ID=xxx
export AUTH_GITHUB_CLIENT_SECRET=xxx
# export AUTH_GITHUB_ENTERPRISE_INSTANCE_URL=https://github.{MY_BIZ}.com
@@ -315,7 +315,7 @@ export AUTH_GITHUB_CLIENT_SECRET=xxx
> ...
```
-6. Open and change _root > packages > app > src >_`App.tsx` to use correct
+5. Open and change _root > packages > app > src >`App.tsx` to use correct
authentication provider reference
```tsx
@@ -332,9 +332,9 @@ Modify the imported reference based on authentication method selected above
| Microsoft | microsoftAuthApiRef |
| Auth0 | googleAuthApiRef |
-7. In the same file, modify createApp
+6. In the same file, modify createApp
-Remeber to modify the provider information based on the table above.
+Remember to modify the provider information based on the table above.
```tsx
const app = createApp({
From b62bc928de9693d2af8e7cd131018f6d522021ed Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Mon, 11 Jan 2021 09:41:06 +0100
Subject: [PATCH 03/92] Run prettier.
---
docs/tutorials/quickstart-app-auth.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 5991f626e7..b6370fd1f9 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -315,7 +315,7 @@ export AUTH_GITHUB_CLIENT_SECRET=xxx
> ...
```
-5. Open and change _root > packages > app > src >`App.tsx` to use correct
+5. Open and change \_root > packages > app > src >`App.tsx` to use correct
authentication provider reference
```tsx
From 34206f81f05459a675c40e0351cdd0b0de8b97ed Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Mon, 11 Jan 2021 13:47:47 +0100
Subject: [PATCH 04/92] Adding reference to latest node LTS as well.
Resolves:
* https://github.com/backstage/backstage/pull/4003#discussion_r554920635
---
docs/tutorials/quickstart-app-auth.md | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index b6370fd1f9..48b5d2b3a7 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -11,9 +11,10 @@ title: Monorepo App Setup With Authentication
> own environment. It starts with a skeleton install and verifying of the
> monorepo's functionality. Next, authentication is added and tested.
>
-> This document assumes you have Node.js 12 active along with Yarn and Python.
-> Please note, that at the time of this writing, the current version is v0.4.5
-> This guide can still be used with future versions, just, verify as you go.
+> This document assumes you have Node.js 12 or 14 active along with Yarn and
+> Python. Please note, that at the time of this writing, the current version is
+> v0.4.5 This guide can still be used with future versions, just, verify as you
+> go.
# The Skeleton Application
From 82e35cd6531b9b62d4b264dd831a5585fc2e24b0 Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Mon, 11 Jan 2021 16:57:58 +0100
Subject: [PATCH 05/92] Update docs/tutorials/quickstart-app-auth.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Fredrik Adelöw
---
docs/tutorials/quickstart-app-auth.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 48b5d2b3a7..88ccce6ae5 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -13,7 +13,7 @@ title: Monorepo App Setup With Authentication
>
> This document assumes you have Node.js 12 or 14 active along with Yarn and
> Python. Please note, that at the time of this writing, the current version is
-> v0.4.5 This guide can still be used with future versions, just, verify as you
+> v0.4.5. This guide can still be used with future versions, just, verify as you
> go.
# The Skeleton Application
From ff4e53bd4ebb257732b0374cc6b2d19988e1cfe7 Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Mon, 11 Jan 2021 16:58:08 +0100
Subject: [PATCH 06/92] Update docs/tutorials/quickstart-app-auth.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Fredrik Adelöw
---
docs/tutorials/quickstart-app-auth.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 88ccce6ae5..2affcec7fc 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -53,7 +53,7 @@ guest. Let's fix that now and add auth.
# The Auth Configuration
-Default Backstage installation includes multiple authentication providers out of
+A default Backstage installation includes multiple authentication providers out of
the box. The steps to enable new authentication provider in Backstage are very
similar to each other, the biggest difference is usually configuring the
external authentication provider. Please see a subset of possible providers and
From 322b04bff7191e0f31591a9306888e140b48f7fb Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Mon, 11 Jan 2021 16:58:14 +0100
Subject: [PATCH 07/92] Update docs/tutorials/quickstart-app-auth.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Fredrik Adelöw
---
docs/tutorials/quickstart-app-auth.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 2affcec7fc..12d3767791 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -54,7 +54,7 @@ guest. Let's fix that now and add auth.
# The Auth Configuration
A default Backstage installation includes multiple authentication providers out of
-the box. The steps to enable new authentication provider in Backstage are very
+the box. The steps to enable new authentication providers in Backstage are very
similar to each other, the biggest difference is usually configuring the
external authentication provider. Please see a subset of possible providers and
instructions to integrate them below. Steps 1 & 2 are described separately for
From f3165cce9535a17d7698a584c90397d8ba351dfe Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Mon, 11 Jan 2021 16:58:24 +0100
Subject: [PATCH 08/92] Update docs/tutorials/quickstart-app-auth.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Fredrik Adelöw
---
docs/tutorials/quickstart-app-auth.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 12d3767791..7e05a5a63d 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -88,7 +88,7 @@ auth:
# $env: AUTH_GITHUB_ENTERPRISE_INSTANCE_URL
```
-2. Generate Github client id and secret
+2. Generate a GitHub client ID and secret
- Log into http://github.com
- Navigate to (Settings > Developer Settings > OAuth Apps > New OAuth
From 642bf0be1a47df153704e32e8304fe98ef94ce65 Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Mon, 11 Jan 2021 16:59:17 +0100
Subject: [PATCH 09/92] Update docs/tutorials/quickstart-app-auth.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Fredrik Adelöw
---
docs/tutorials/quickstart-app-auth.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 7e05a5a63d..0f5e452aa5 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -103,7 +103,7 @@ auth:
-Gitlab
+GitLab
1. Open `app-config.yaml` and change it as follows
From 72399d57e5612b6198bce30efd2910ab67effef4 Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Mon, 11 Jan 2021 16:59:38 +0100
Subject: [PATCH 10/92] Update docs/tutorials/quickstart-app-auth.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Fredrik Adelöw
---
docs/tutorials/quickstart-app-auth.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 0f5e452aa5..2d23402edd 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -129,7 +129,7 @@ auth:
audience: https://gitlab.com # Or your self-hosted Gitlab instance URL
```
-2. Generate Gitlab Application for client id and secret
+2. Generate a GitLab Application client ID and secret
- Log into Gitlab
- Navigate to (Profile > Settings >
From cc64069374adea4d24e4781f2ca64cb4fb8d0da5 Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Mon, 11 Jan 2021 16:59:51 +0100
Subject: [PATCH 11/92] Update docs/tutorials/quickstart-app-auth.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Fredrik Adelöw
---
docs/tutorials/quickstart-app-auth.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 2d23402edd..b58596b434 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -131,7 +131,7 @@ auth:
2. Generate a GitLab Application client ID and secret
-- Log into Gitlab
+- Log into GitLab
- Navigate to (Profile > Settings >
Applications)[https://gitlab.com/-/profile/applications]
- Name your application
From 163e263d8eb8e06868f7d464ba86b3adfaf76a23 Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Mon, 11 Jan 2021 17:00:35 +0100
Subject: [PATCH 12/92] Update docs/tutorials/quickstart-app-auth.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Fredrik Adelöw
---
docs/tutorials/quickstart-app-auth.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index b58596b434..00c97cfe4b 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -180,7 +180,7 @@ auth:
- Log into https://console.cloud.google.com
- Select or create a new project from the dropdown on the top bar
-- Navigate to (APIs & Services - >
+- Navigate to (APIs & Services >
Credentials)[https://console.cloud.google.com/apis/credentials]
- Add new Authorised JavaScript origin = `http://localhost:3000`
- Add new Authorised redirect URI =
From ecba810c98a37c72a915061f5822eec338db62a2 Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Mon, 11 Jan 2021 17:01:32 +0100
Subject: [PATCH 13/92] Update docs/tutorials/quickstart-app-auth.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Fredrik Adelöw
---
docs/tutorials/quickstart-app-auth.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 00c97cfe4b..1cae03469d 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -323,7 +323,7 @@ export AUTH_GITHUB_CLIENT_SECRET=xxx
import { githubAuthApiRef, SignInPage } from '@backstage/core';
```
-Modify the imported reference based on authentication method selected above
+Modify the imported reference based on the authentication method you selected above:
| Auth Provider | Import Name |
| ------------- | ------------------- |
From 33267d1cb9f398153634a2dfb0851b5edfc488c8 Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Mon, 11 Jan 2021 17:01:39 +0100
Subject: [PATCH 14/92] Update docs/tutorials/quickstart-app-auth.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Fredrik Adelöw
---
docs/tutorials/quickstart-app-auth.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 1cae03469d..900c52c484 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -331,7 +331,7 @@ Modify the imported reference based on the authentication method you selected ab
| Gitlab | gitlabAuthApiRef |
| Google | googleAuthApiRef |
| Microsoft | microsoftAuthApiRef |
-| Auth0 | googleAuthApiRef |
+| Auth0 | auth0AuthApiRef |
6. In the same file, modify createApp
From 39b45b62b4889953dcf8676cf61b75d5f3a51876 Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Mon, 11 Jan 2021 17:01:49 +0100
Subject: [PATCH 15/92] Update docs/tutorials/quickstart-app-auth.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Fredrik Adelöw
---
docs/tutorials/quickstart-app-auth.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 900c52c484..db97ae5ed4 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -369,8 +369,8 @@ When the browser loads, you should be presented with a login page for GitHub.
Login as usual with your GitHub account. If this is your first time, you will be
asked to authorize and then are redirected to the catalog page if all is well.
-For more information you can clone the repository:
-https://github.com/RoadieHQ/backstage-auth-example Each authentication setting
+For more information you can clone [the backstage-auth-example repository](https://github.com/RoadieHQ/backstage-auth-example).
+ Each authentication setting
is set up there on a branch named after the authentication provider.
# Where to go from here
From dc48cfc9f2d862f8ec805ffc4fc67d0ae33b0e27 Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Tue, 12 Jan 2021 08:27:31 +0100
Subject: [PATCH 16/92] Update docs/tutorials/quickstart-app-auth.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Fredrik Adelöw
---
docs/tutorials/quickstart-app-auth.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index db97ae5ed4..364e4a434a 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -268,7 +268,7 @@ auth:
$env: AUTH_AUTH0_DOMAIN_ID
```
-2. Create Auth0 application in Auth0 management console
+2. Create an Auth0 application in the Auth0 management console
- Log into https://manage.auth0.com/dashboard/
- Navigate to Applications
From 4f0993407b406037d0468a77638d82da6993ed51 Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Tue, 12 Jan 2021 08:31:32 +0100
Subject: [PATCH 17/92] Update docs/tutorials/quickstart-app-auth.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Fredrik Adelöw
---
docs/tutorials/quickstart-app-auth.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 364e4a434a..0fd48dc7bf 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -223,7 +223,7 @@ auth:
2. Create Microsoft Directory in Microsoft Portal
- Log into https://portal.azure.com
-- Navigate to (Azure Active Directory -> App
+- Navigate to (Azure Active Directory > App
Registrations)[https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps]
- Create a New Registration
- Add new Redirect URI = `http://localhost:3000`
From 80619474d854e3f1e1f8205920fa43cae513fc28 Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Tue, 12 Jan 2021 08:46:22 +0100
Subject: [PATCH 18/92] Update docs/tutorials/quickstart-app-auth.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Fredrik Adelöw
---
docs/tutorials/quickstart-app-auth.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 0fd48dc7bf..2ad2d7c900 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -220,7 +220,7 @@ auth:
$env: AUTH_MICROSOFT_TENANT_ID
```
-2. Create Microsoft Directory in Microsoft Portal
+2. Create a Microsoft App Registration in Microsoft Portal
- Log into https://portal.azure.com
- Navigate to (Azure Active Directory > App
From 0fed686aed8791e0ce24911df04193bb44b34e3e Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Tue, 12 Jan 2021 08:47:27 +0100
Subject: [PATCH 19/92] Update docs/tutorials/quickstart-app-auth.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Fredrik Adelöw
---
docs/tutorials/quickstart-app-auth.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 2ad2d7c900..7475061b9e 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -176,7 +176,7 @@ auth:
$env: AUTH_GOOGLE_CLIENT_SECRET
```
-2. Generate Google Application in Google Cloud console
+2. Generate Google Credentials in Google Cloud console
- Log into https://console.cloud.google.com
- Select or create a new project from the dropdown on the top bar
From 6d74776e1214f1e9ffe187f89cf909cbfdd027f1 Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Tue, 12 Jan 2021 08:55:45 +0100
Subject: [PATCH 20/92] Adds in more modifications based on PR comments
Run prettier.
Add heading level for list items.
Fix styling and nomenclature.
---
docs/tutorials/quickstart-app-auth.md | 68 +++++++++++++++------------
1 file changed, 38 insertions(+), 30 deletions(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 7475061b9e..205fcddb65 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -53,9 +53,9 @@ guest. Let's fix that now and add auth.
# The Auth Configuration
-A default Backstage installation includes multiple authentication providers out of
-the box. The steps to enable new authentication providers in Backstage are very
-similar to each other, the biggest difference is usually configuring the
+A default Backstage installation includes multiple authentication providers out
+of the box. The steps to enable new authentication providers in Backstage are
+very similar to each other, the biggest difference is usually configuring the
external authentication provider. Please see a subset of possible providers and
instructions to integrate them below. Steps 1 & 2 are described separately for
each provider and steps beyond that are common for all.
@@ -63,7 +63,7 @@ each provider and steps beyond that are common for all.
Github
-1. Open `app-config.yaml` and change it as follows
+### 1. Open `app-config.yaml` and change it as follows
_from:_
@@ -88,7 +88,7 @@ auth:
# $env: AUTH_GITHUB_ENTERPRISE_INSTANCE_URL
```
-2. Generate a GitHub client ID and secret
+### 2. Generate a GitHub client ID and secret
- Log into http://github.com
- Navigate to (Settings > Developer Settings > OAuth Apps > New OAuth
@@ -106,7 +106,7 @@ auth:
GitLab
-1. Open `app-config.yaml` and change it as follows
+### 1. Open `app-config.yaml` and change it as follows
_from:_
@@ -129,7 +129,7 @@ auth:
audience: https://gitlab.com # Or your self-hosted Gitlab instance URL
```
-2. Generate a GitLab Application client ID and secret
+### 2. Generate a Gitlab Application client ID and secret
- Log into GitLab
- Navigate to (Profile > Settings >
@@ -137,12 +137,14 @@ auth:
- Name your application
- Set Callback URL = http://localhost:7000/api/auth/gitlab/handler/frame
- Select the following values:
- - `read_user (Read the authenticated user's personal information)`
- - `read_repository (Allows read-only access to the repository)`
- - `write_repository (Allows read-write access to the repository)`
- - `openid (Authenticate using OpenID Connect)`
- - `profile (Allows read-only access to the user's personal information using OpenID Connect)`
- - `email (Allows read-only access to the user's primary email address using OpenID Connect)`
+ - `read_user` (Read the authenticated user's personal information)
+ - `read_repository` (Allows read-only access to the repository)
+ - `write_repository` (Allows read-write access to the repository)
+ - `openid` (Authenticate using OpenID Connect)
+ - `profile` (Allows read-only access to the user's personal information using
+ OpenID Connect)
+ - `email` (Allows read-only access to the user's primary email address using
+ OpenID Connect)
- Click [Save application]
- On the next page, copy and paste your new Application ID and Secret to
environment variables defined in the `app-config.yaml` file,
@@ -154,7 +156,7 @@ auth:
Google
-1. Open `app-config.yaml` and change it as follows
+### 1. Open `app-config.yaml` and change it as follows
_from:_
@@ -176,12 +178,14 @@ auth:
$env: AUTH_GOOGLE_CLIENT_SECRET
```
-2. Generate Google Credentials in Google Cloud console
+### 2. Generate Google Credentials in Google Cloud console
- Log into https://console.cloud.google.com
- Select or create a new project from the dropdown on the top bar
- Navigate to (APIs & Services >
Credentials)[https://console.cloud.google.com/apis/credentials]
+- Click Create Credentials and select [OAuth client ID]
+- Select Web Application as the application type
- Add new Authorised JavaScript origin = `http://localhost:3000`
- Add new Authorised redirect URI =
`http://localhost:7000/api/auth/google/handler/frame`
@@ -196,7 +200,7 @@ auth:
Microsoft
-1. Open `app-config.yaml` and change it as follows
+### 1. Open `app-config.yaml` and change it as follows
_from:_
@@ -220,7 +224,7 @@ auth:
$env: AUTH_MICROSOFT_TENANT_ID
```
-2. Create a Microsoft App Registration in Microsoft Portal
+### 2. Create a Microsoft App Registration in Microsoft Portal
- Log into https://portal.azure.com
- Navigate to (Azure Active Directory > App
@@ -244,7 +248,7 @@ auth:
Auth0
-1. Open `app-config.yaml` and change it as follows
+### 1. Open `app-config.yaml` and change it as follows
_from:_
@@ -268,7 +272,7 @@ auth:
$env: AUTH_AUTH0_DOMAIN_ID
```
-2. Create an Auth0 application in the Auth0 management console
+### 2. Create an Auth0 application in the Auth0 management console
- Log into https://manage.auth0.com/dashboard/
- Navigate to Applications
@@ -287,8 +291,9 @@ auth:
-3. Set environment variables in whatever fashion is easiest for you. I chose to
- add mine to my `.zshrc` profile.
+### 3. Set environment variables in whatever fashion is easiest for you. I chose to
+
+add mine to my `.zshrc` profile.
```zsh
# For macOS Catalina & Z Shell
@@ -300,7 +305,7 @@ export AUTH_GITHUB_CLIENT_SECRET=xxx
# export AUTH_GITHUB_ENTERPRISE_INSTANCE_URL=https://github.{MY_BIZ}.com
```
-4. And of course I need to source that file.
+### 4. And of course I need to source that file.
```zsh
# Loading the new variables
@@ -316,14 +321,16 @@ export AUTH_GITHUB_CLIENT_SECRET=xxx
> ...
```
-5. Open and change \_root > packages > app > src >`App.tsx` to use correct
- authentication provider reference
+### 5. Open and change _root > packages > app > src >_ `App.tsx` to use correct
+
+authentication provider reference
```tsx
import { githubAuthApiRef, SignInPage } from '@backstage/core';
```
-Modify the imported reference based on the authentication method you selected above:
+Modify the imported reference based on the authentication method you selected
+above:
| Auth Provider | Import Name |
| ------------- | ------------------- |
@@ -331,9 +338,9 @@ Modify the imported reference based on the authentication method you selected ab
| Gitlab | gitlabAuthApiRef |
| Google | googleAuthApiRef |
| Microsoft | microsoftAuthApiRef |
-| Auth0 | auth0AuthApiRef |
+| Auth0 | auth0AuthApiRef |
-6. In the same file, modify createApp
+### 6. In the same file, modify createApp
Remember to modify the provider information based on the table above.
@@ -369,9 +376,10 @@ When the browser loads, you should be presented with a login page for GitHub.
Login as usual with your GitHub account. If this is your first time, you will be
asked to authorize and then are redirected to the catalog page if all is well.
-For more information you can clone [the backstage-auth-example repository](https://github.com/RoadieHQ/backstage-auth-example).
- Each authentication setting
-is set up there on a branch named after the authentication provider.
+For more information you can clone
+[the backstage-auth-example repository](https://github.com/RoadieHQ/backstage-auth-example).
+Each authentication setting is set up there on a branch named after the
+authentication provider.
# Where to go from here
From abbee6fff46a6ffc866df086063a4bf41877999f Mon Sep 17 00:00:00 2001
From: Oliver Sand
Date: Tue, 12 Jan 2021 17:05:49 +0100
Subject: [PATCH 21/92] Add system, domain and resource entity kinds
---
.changeset/thin-icons-kick.md | 6 +
app-config.yaml | 2 +-
.../src/kinds/ApiEntityV1alpha1.test.ts | 16 ++
.../src/kinds/ApiEntityV1alpha1.ts | 2 +
.../src/kinds/ComponentEntityV1alpha1.test.ts | 16 ++
.../src/kinds/ComponentEntityV1alpha1.ts | 2 +
.../src/kinds/DomainEntityV1alpha1.test.ts | 71 ++++++++
.../src/kinds/DomainEntityV1alpha1.ts | 46 +++++
.../src/kinds/ResourceEntityV1alpha1.test.ts | 103 +++++++++++
.../src/kinds/ResourceEntityV1alpha1.ts | 50 ++++++
.../src/kinds/SystemEntityV1alpha1.test.ts | 87 +++++++++
.../src/kinds/SystemEntityV1alpha1.ts | 48 +++++
packages/catalog-model/src/kinds/index.ts | 15 ++
packages/catalog-model/src/kinds/relations.ts | 7 +-
.../BuiltinKindsEntityProcessor.test.ts | 169 +++++++++++++++++-
.../processors/BuiltinKindsEntityProcessor.ts | 79 +++++++-
16 files changed, 713 insertions(+), 6 deletions(-)
create mode 100644 .changeset/thin-icons-kick.md
create mode 100644 packages/catalog-model/src/kinds/DomainEntityV1alpha1.test.ts
create mode 100644 packages/catalog-model/src/kinds/DomainEntityV1alpha1.ts
create mode 100644 packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts
create mode 100644 packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts
create mode 100644 packages/catalog-model/src/kinds/SystemEntityV1alpha1.test.ts
create mode 100644 packages/catalog-model/src/kinds/SystemEntityV1alpha1.ts
diff --git a/.changeset/thin-icons-kick.md b/.changeset/thin-icons-kick.md
new file mode 100644
index 0000000000..348f55a8ad
--- /dev/null
+++ b/.changeset/thin-icons-kick.md
@@ -0,0 +1,6 @@
+---
+'@backstage/catalog-model': patch
+'@backstage/plugin-catalog-backend': patch
+---
+
+Implement System, Domain and Resource entity kinds.
diff --git a/app-config.yaml b/app-config.yaml
index 48869cc0a0..b67e9525bf 100644
--- a/app-config.yaml
+++ b/app-config.yaml
@@ -127,7 +127,7 @@ integrations:
catalog:
rules:
- - allow: [Component, API, Group, User, Template, Location]
+ - allow: [Component, API, Resource, Group, User, Template, System, Domain, Location]
processors:
githubOrg:
diff --git a/packages/catalog-model/src/kinds/ApiEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/ApiEntityV1alpha1.test.ts
index a5d5152fab..a4d7d904cd 100644
--- a/packages/catalog-model/src/kinds/ApiEntityV1alpha1.test.ts
+++ b/packages/catalog-model/src/kinds/ApiEntityV1alpha1.test.ts
@@ -70,6 +70,7 @@ components:
items:
$ref: "#/components/schemas/Pet"
`,
+ system: 'system',
},
};
});
@@ -152,4 +153,19 @@ components:
(entity as any).spec.definition = '';
await expect(validator.check(entity)).rejects.toThrow(/definition/);
});
+
+ it('accepts missing system', async () => {
+ delete (entity as any).spec.system;
+ await expect(validator.check(entity)).resolves.toBe(true);
+ });
+
+ it('rejects wrong system', async () => {
+ (entity as any).spec.system = 7;
+ await expect(validator.check(entity)).rejects.toThrow(/system/);
+ });
+
+ it('rejects empty system', async () => {
+ (entity as any).spec.system = '';
+ await expect(validator.check(entity)).rejects.toThrow(/system/);
+ });
});
diff --git a/packages/catalog-model/src/kinds/ApiEntityV1alpha1.ts b/packages/catalog-model/src/kinds/ApiEntityV1alpha1.ts
index 660cd71cd8..2c634ff091 100644
--- a/packages/catalog-model/src/kinds/ApiEntityV1alpha1.ts
+++ b/packages/catalog-model/src/kinds/ApiEntityV1alpha1.ts
@@ -30,6 +30,7 @@ const schema = yup.object>({
lifecycle: yup.string().required().min(1),
owner: yup.string().required().min(1),
definition: yup.string().required().min(1),
+ system: yup.string().notRequired().min(1),
})
.required(),
});
@@ -42,6 +43,7 @@ export interface ApiEntityV1alpha1 extends Entity {
lifecycle: string;
owner: string;
definition: string;
+ system?: string;
};
}
diff --git a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts
index 10d66ac880..9284a5d5b1 100644
--- a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts
+++ b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts
@@ -36,6 +36,7 @@ describe('ComponentV1alpha1Validator', () => {
subcomponentOf: 'monolith',
providesApis: ['api-0'],
consumesApis: ['api-0'],
+ system: 'system',
},
};
});
@@ -158,4 +159,19 @@ describe('ComponentV1alpha1Validator', () => {
(entity as any).spec.consumesApis = [];
await expect(validator.check(entity)).resolves.toBe(true);
});
+
+ it('accepts missing system', async () => {
+ delete (entity as any).spec.system;
+ await expect(validator.check(entity)).resolves.toBe(true);
+ });
+
+ it('rejects wrong system', async () => {
+ (entity as any).spec.system = 7;
+ await expect(validator.check(entity)).rejects.toThrow(/system/);
+ });
+
+ it('rejects empty system', async () => {
+ (entity as any).spec.system = '';
+ await expect(validator.check(entity)).rejects.toThrow(/system/);
+ });
});
diff --git a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts
index 97519ad403..c55c48055a 100644
--- a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts
+++ b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts
@@ -32,6 +32,7 @@ const schema = yup.object>({
subcomponentOf: yup.string().notRequired().min(1),
providesApis: yup.array(yup.string().required()).notRequired(),
consumesApis: yup.array(yup.string().required()).notRequired(),
+ system: yup.string().notRequired().min(1),
})
.required(),
});
@@ -46,6 +47,7 @@ export interface ComponentEntityV1alpha1 extends Entity {
subcomponentOf?: string;
providesApis?: string[];
consumesApis?: string[];
+ system?: string;
};
}
diff --git a/packages/catalog-model/src/kinds/DomainEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/DomainEntityV1alpha1.test.ts
new file mode 100644
index 0000000000..0e989f22ca
--- /dev/null
+++ b/packages/catalog-model/src/kinds/DomainEntityV1alpha1.test.ts
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+ DomainEntityV1alpha1,
+ domainEntityV1alpha1Validator as validator,
+} from './DomainEntityV1alpha1';
+
+describe('DomainV1alpha1Validator', () => {
+ let entity: DomainEntityV1alpha1;
+
+ beforeEach(() => {
+ entity = {
+ apiVersion: 'backstage.io/v1alpha1',
+ kind: 'Domain',
+ metadata: {
+ name: 'test',
+ },
+ spec: {
+ owner: 'me',
+ },
+ };
+ });
+
+ it('happy path: accepts valid data', async () => {
+ await expect(validator.check(entity)).resolves.toBe(true);
+ });
+
+ it('silently accepts v1beta1 as well', async () => {
+ (entity as any).apiVersion = 'backstage.io/v1beta1';
+ await expect(validator.check(entity)).resolves.toBe(true);
+ });
+
+ it('ignores unknown apiVersion', async () => {
+ (entity as any).apiVersion = 'backstage.io/v1beta0';
+ await expect(validator.check(entity)).resolves.toBe(false);
+ });
+
+ it('ignores unknown kind', async () => {
+ (entity as any).kind = 'Wizard';
+ await expect(validator.check(entity)).resolves.toBe(false);
+ });
+
+ it('rejects missing owner', async () => {
+ delete (entity as any).spec.owner;
+ await expect(validator.check(entity)).rejects.toThrow(/owner/);
+ });
+
+ it('rejects wrong owner', async () => {
+ (entity as any).spec.owner = 7;
+ await expect(validator.check(entity)).rejects.toThrow(/owner/);
+ });
+
+ it('rejects empty owner', async () => {
+ (entity as any).spec.owner = '';
+ await expect(validator.check(entity)).rejects.toThrow(/owner/);
+ });
+});
diff --git a/packages/catalog-model/src/kinds/DomainEntityV1alpha1.ts b/packages/catalog-model/src/kinds/DomainEntityV1alpha1.ts
new file mode 100644
index 0000000000..60b11aa124
--- /dev/null
+++ b/packages/catalog-model/src/kinds/DomainEntityV1alpha1.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as yup from 'yup';
+import type { Entity } from '../entity/Entity';
+import { schemaValidator } from './util';
+
+const API_VERSION = ['backstage.io/v1alpha1', 'backstage.io/v1beta1'] as const;
+const KIND = 'Domain' as const;
+
+const schema = yup.object>({
+ apiVersion: yup.string().required().oneOf(API_VERSION),
+ kind: yup.string().required().equals([KIND]),
+ spec: yup
+ .object({
+ owner: yup.string().required().min(1),
+ })
+ .required(),
+});
+
+export interface DomainEntityV1alpha1 extends Entity {
+ apiVersion: typeof API_VERSION[number];
+ kind: typeof KIND;
+ spec: {
+ owner: string;
+ };
+}
+
+export const domainEntityV1alpha1Validator = schemaValidator(
+ KIND,
+ API_VERSION,
+ schema,
+);
diff --git a/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts
new file mode 100644
index 0000000000..ad8ea5cdf3
--- /dev/null
+++ b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+ ResourceEntityV1alpha1,
+ resourceEntityV1alpha1Validator as validator,
+} from './ResourceEntityV1alpha1';
+
+describe('ResourceV1alpha1Validator', () => {
+ let entity: ResourceEntityV1alpha1;
+
+ beforeEach(() => {
+ entity = {
+ apiVersion: 'backstage.io/v1alpha1',
+ kind: 'Resource',
+ metadata: {
+ name: 'test',
+ },
+ spec: {
+ type: 'database',
+ owner: 'me',
+ system: 'system',
+ },
+ };
+ });
+
+ it('happy path: accepts valid data', async () => {
+ await expect(validator.check(entity)).resolves.toBe(true);
+ });
+
+ it('silently accepts v1beta1 as well', async () => {
+ (entity as any).apiVersion = 'backstage.io/v1beta1';
+ await expect(validator.check(entity)).resolves.toBe(true);
+ });
+
+ it('ignores unknown apiVersion', async () => {
+ (entity as any).apiVersion = 'backstage.io/v1beta0';
+ await expect(validator.check(entity)).resolves.toBe(false);
+ });
+
+ it('ignores unknown kind', async () => {
+ (entity as any).kind = 'Wizard';
+ await expect(validator.check(entity)).resolves.toBe(false);
+ });
+
+ it('rejects missing type', async () => {
+ delete (entity as any).spec.type;
+ await expect(validator.check(entity)).rejects.toThrow(/type/);
+ });
+
+ it('rejects wrong type', async () => {
+ (entity as any).spec.type = 7;
+ await expect(validator.check(entity)).rejects.toThrow(/type/);
+ });
+
+ it('rejects empty type', async () => {
+ (entity as any).spec.type = '';
+ await expect(validator.check(entity)).rejects.toThrow(/type/);
+ });
+
+ it('rejects missing owner', async () => {
+ delete (entity as any).spec.owner;
+ await expect(validator.check(entity)).rejects.toThrow(/owner/);
+ });
+
+ it('rejects wrong owner', async () => {
+ (entity as any).spec.owner = 7;
+ await expect(validator.check(entity)).rejects.toThrow(/owner/);
+ });
+
+ it('rejects empty owner', async () => {
+ (entity as any).spec.owner = '';
+ await expect(validator.check(entity)).rejects.toThrow(/owner/);
+ });
+
+ it('accepts missing system', async () => {
+ delete (entity as any).spec.system;
+ await expect(validator.check(entity)).resolves.toBe(true);
+ });
+
+ it('rejects wrong system', async () => {
+ (entity as any).spec.system = 7;
+ await expect(validator.check(entity)).rejects.toThrow(/system/);
+ });
+
+ it('rejects empty system', async () => {
+ (entity as any).spec.system = '';
+ await expect(validator.check(entity)).rejects.toThrow(/system/);
+ });
+});
diff --git a/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts
new file mode 100644
index 0000000000..12df7f6664
--- /dev/null
+++ b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as yup from 'yup';
+import type { Entity } from '../entity/Entity';
+import { schemaValidator } from './util';
+
+const API_VERSION = ['backstage.io/v1alpha1', 'backstage.io/v1beta1'] as const;
+const KIND = 'Resource' as const;
+
+const schema = yup.object>({
+ apiVersion: yup.string().required().oneOf(API_VERSION),
+ kind: yup.string().required().equals([KIND]),
+ spec: yup
+ .object({
+ type: yup.string().required().min(1),
+ owner: yup.string().required().min(1),
+ system: yup.string().notRequired().min(1),
+ })
+ .required(),
+});
+
+export interface ResourceEntityV1alpha1 extends Entity {
+ apiVersion: typeof API_VERSION[number];
+ kind: typeof KIND;
+ spec: {
+ type: string;
+ owner: string;
+ system?: string;
+ };
+}
+
+export const resourceEntityV1alpha1Validator = schemaValidator(
+ KIND,
+ API_VERSION,
+ schema,
+);
diff --git a/packages/catalog-model/src/kinds/SystemEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/SystemEntityV1alpha1.test.ts
new file mode 100644
index 0000000000..7d744b7d0d
--- /dev/null
+++ b/packages/catalog-model/src/kinds/SystemEntityV1alpha1.test.ts
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+ SystemEntityV1alpha1,
+ systemEntityV1alpha1Validator as validator,
+} from './SystemEntityV1alpha1';
+
+describe('SystemV1alpha1Validator', () => {
+ let entity: SystemEntityV1alpha1;
+
+ beforeEach(() => {
+ entity = {
+ apiVersion: 'backstage.io/v1alpha1',
+ kind: 'System',
+ metadata: {
+ name: 'test',
+ },
+ spec: {
+ owner: 'me',
+ domain: 'domain',
+ },
+ };
+ });
+
+ it('happy path: accepts valid data', async () => {
+ await expect(validator.check(entity)).resolves.toBe(true);
+ });
+
+ it('silently accepts v1beta1 as well', async () => {
+ (entity as any).apiVersion = 'backstage.io/v1beta1';
+ await expect(validator.check(entity)).resolves.toBe(true);
+ });
+
+ it('ignores unknown apiVersion', async () => {
+ (entity as any).apiVersion = 'backstage.io/v1beta0';
+ await expect(validator.check(entity)).resolves.toBe(false);
+ });
+
+ it('ignores unknown kind', async () => {
+ (entity as any).kind = 'Wizard';
+ await expect(validator.check(entity)).resolves.toBe(false);
+ });
+
+ it('rejects missing owner', async () => {
+ delete (entity as any).spec.owner;
+ await expect(validator.check(entity)).rejects.toThrow(/owner/);
+ });
+
+ it('rejects wrong owner', async () => {
+ (entity as any).spec.owner = 7;
+ await expect(validator.check(entity)).rejects.toThrow(/owner/);
+ });
+
+ it('rejects empty owner', async () => {
+ (entity as any).spec.owner = '';
+ await expect(validator.check(entity)).rejects.toThrow(/owner/);
+ });
+
+ it('accepts missing domain', async () => {
+ delete (entity as any).spec.domain;
+ await expect(validator.check(entity)).resolves.toBe(true);
+ });
+
+ it('rejects wrong domain', async () => {
+ (entity as any).spec.domain = 7;
+ await expect(validator.check(entity)).rejects.toThrow(/domain/);
+ });
+
+ it('rejects empty domain', async () => {
+ (entity as any).spec.domain = '';
+ await expect(validator.check(entity)).rejects.toThrow(/domain/);
+ });
+});
diff --git a/packages/catalog-model/src/kinds/SystemEntityV1alpha1.ts b/packages/catalog-model/src/kinds/SystemEntityV1alpha1.ts
new file mode 100644
index 0000000000..764514efdd
--- /dev/null
+++ b/packages/catalog-model/src/kinds/SystemEntityV1alpha1.ts
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as yup from 'yup';
+import type { Entity } from '../entity/Entity';
+import { schemaValidator } from './util';
+
+const API_VERSION = ['backstage.io/v1alpha1', 'backstage.io/v1beta1'] as const;
+const KIND = 'System' as const;
+
+const schema = yup.object>({
+ apiVersion: yup.string().required().oneOf(API_VERSION),
+ kind: yup.string().required().equals([KIND]),
+ spec: yup
+ .object({
+ owner: yup.string().required().min(1),
+ domain: yup.string().notRequired().min(1),
+ })
+ .required(),
+});
+
+export interface SystemEntityV1alpha1 extends Entity {
+ apiVersion: typeof API_VERSION[number];
+ kind: typeof KIND;
+ spec: {
+ owner: string;
+ domain?: string;
+ };
+}
+
+export const systemEntityV1alpha1Validator = schemaValidator(
+ KIND,
+ API_VERSION,
+ schema,
+);
diff --git a/packages/catalog-model/src/kinds/index.ts b/packages/catalog-model/src/kinds/index.ts
index e00a49acb5..bc157c79df 100644
--- a/packages/catalog-model/src/kinds/index.ts
+++ b/packages/catalog-model/src/kinds/index.ts
@@ -26,6 +26,11 @@ export type {
ComponentEntityV1alpha1 as ComponentEntity,
ComponentEntityV1alpha1,
} from './ComponentEntityV1alpha1';
+export { domainEntityV1alpha1Validator } from './DomainEntityV1alpha1';
+export type {
+ DomainEntityV1alpha1 as DomainEntity,
+ DomainEntityV1alpha1,
+} from './DomainEntityV1alpha1';
export { groupEntityV1alpha1Validator } from './GroupEntityV1alpha1';
export type {
GroupEntityV1alpha1 as GroupEntity,
@@ -37,6 +42,16 @@ export type {
LocationEntityV1alpha1,
} from './LocationEntityV1alpha1';
export * from './relations';
+export { resourceEntityV1alpha1Validator } from './ResourceEntityV1alpha1';
+export type {
+ ResourceEntityV1alpha1 as ResourceEntity,
+ ResourceEntityV1alpha1,
+} from './ResourceEntityV1alpha1';
+export { systemEntityV1alpha1Validator } from './SystemEntityV1alpha1';
+export type {
+ SystemEntityV1alpha1 as SystemEntity,
+ SystemEntityV1alpha1,
+} from './SystemEntityV1alpha1';
export { templateEntityV1alpha1Validator } from './TemplateEntityV1alpha1';
export type {
TemplateEntityV1alpha1 as TemplateEntity,
diff --git a/packages/catalog-model/src/kinds/relations.ts b/packages/catalog-model/src/kinds/relations.ts
index 78bbc61df2..ed40a7e9c6 100644
--- a/packages/catalog-model/src/kinds/relations.ts
+++ b/packages/catalog-model/src/kinds/relations.ts
@@ -30,7 +30,7 @@ export const RELATION_OWNED_BY = 'ownedBy';
export const RELATION_OWNER_OF = 'ownerOf';
/**
- * A relation with an API entity, typically from a component or system
+ * A relation with an API entity, typically from a component
*/
export const RELATION_CONSUMES_API = 'consumesApi';
export const RELATION_API_CONSUMED_BY = 'apiConsumedBy';
@@ -57,8 +57,13 @@ export const RELATION_MEMBER_OF = 'memberOf';
export const RELATION_HAS_MEMBER = 'hasMember';
/**
+<<<<<<< HEAD
* A part/whole relation, typically for components in a system and systems
* in a domain.
+=======
+ * A grouping relation, typically for components, resources or APIs in a
+ * system, or for systems inside a domain.
+>>>>>>> Add system, domain and resource entity kinds
*/
export const RELATION_PART_OF = 'partOf';
export const RELATION_HAS_PART = 'hasPart';
diff --git a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts
index 1d2562c25b..feb4791477 100644
--- a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts
+++ b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts
@@ -17,7 +17,10 @@
import {
ApiEntity,
ComponentEntity,
+ DomainEntity,
GroupEntity,
+ ResourceEntity,
+ SystemEntity,
UserEntity,
} from '@backstage/catalog-model';
import { BuiltinKindsEntityProcessor } from './BuiltinKindsEntityProcessor';
@@ -42,12 +45,13 @@ describe('BuiltinKindsEntityProcessor', () => {
lifecycle: 'l',
providesApis: ['b'],
consumesApis: ['c'],
+ system: 's',
},
};
await processor.postProcessEntity(entity, location, emit);
- expect(emit).toBeCalledTimes(8);
+ expect(emit).toBeCalledTimes(10);
expect(emit).toBeCalledWith({
type: 'relation',
relation: {
@@ -112,6 +116,22 @@ describe('BuiltinKindsEntityProcessor', () => {
target: { kind: 'Component', namespace: 'default', name: 's' },
},
});
+ expect(emit).toBeCalledWith({
+ type: 'relation',
+ relation: {
+ source: { kind: 'System', namespace: 'default', name: 's' },
+ type: 'hasPart',
+ target: { kind: 'Component', namespace: 'default', name: 'n' },
+ },
+ });
+ expect(emit).toBeCalledWith({
+ type: 'relation',
+ relation: {
+ source: { kind: 'Component', namespace: 'default', name: 'n' },
+ type: 'partOf',
+ target: { kind: 'System', namespace: 'default', name: 's' },
+ },
+ });
});
it('generates relations for api entities', async () => {
@@ -124,12 +144,13 @@ describe('BuiltinKindsEntityProcessor', () => {
owner: 'o',
lifecycle: 'l',
definition: 'd',
+ system: 's',
},
};
await processor.postProcessEntity(entity, location, emit);
- expect(emit).toBeCalledTimes(2);
+ expect(emit).toBeCalledTimes(4);
expect(emit).toBeCalledWith({
type: 'relation',
relation: {
@@ -146,6 +167,150 @@ describe('BuiltinKindsEntityProcessor', () => {
target: { kind: 'Group', namespace: 'default', name: 'o' },
},
});
+ expect(emit).toBeCalledWith({
+ type: 'relation',
+ relation: {
+ source: { kind: 'System', namespace: 'default', name: 's' },
+ type: 'hasPart',
+ target: { kind: 'API', namespace: 'default', name: 'n' },
+ },
+ });
+ expect(emit).toBeCalledWith({
+ type: 'relation',
+ relation: {
+ source: { kind: 'API', namespace: 'default', name: 'n' },
+ type: 'partOf',
+ target: { kind: 'System', namespace: 'default', name: 's' },
+ },
+ });
+ });
+
+ it('generates relations for resource entities', async () => {
+ const entity: ResourceEntity = {
+ apiVersion: 'backstage.io/v1alpha1',
+ kind: 'Resource',
+ metadata: { name: 'n' },
+ spec: {
+ type: 'database',
+ owner: 'o',
+ system: 's',
+ },
+ };
+
+ await processor.postProcessEntity(entity, location, emit);
+
+ expect(emit).toBeCalledTimes(4);
+ expect(emit).toBeCalledWith({
+ type: 'relation',
+ relation: {
+ source: { kind: 'Group', namespace: 'default', name: 'o' },
+ type: 'ownerOf',
+ target: { kind: 'Resource', namespace: 'default', name: 'n' },
+ },
+ });
+ expect(emit).toBeCalledWith({
+ type: 'relation',
+ relation: {
+ source: { kind: 'Resource', namespace: 'default', name: 'n' },
+ type: 'ownedBy',
+ target: { kind: 'Group', namespace: 'default', name: 'o' },
+ },
+ });
+ expect(emit).toBeCalledWith({
+ type: 'relation',
+ relation: {
+ source: { kind: 'System', namespace: 'default', name: 's' },
+ type: 'hasPart',
+ target: { kind: 'Resource', namespace: 'default', name: 'n' },
+ },
+ });
+ expect(emit).toBeCalledWith({
+ type: 'relation',
+ relation: {
+ source: { kind: 'Resource', namespace: 'default', name: 'n' },
+ type: 'partOf',
+ target: { kind: 'System', namespace: 'default', name: 's' },
+ },
+ });
+ });
+
+ it('generates relations for system entities', async () => {
+ const entity: SystemEntity = {
+ apiVersion: 'backstage.io/v1alpha1',
+ kind: 'System',
+ metadata: { name: 'n' },
+ spec: {
+ owner: 'o',
+ domain: 'd',
+ },
+ };
+
+ await processor.postProcessEntity(entity, location, emit);
+
+ expect(emit).toBeCalledTimes(4);
+ expect(emit).toBeCalledWith({
+ type: 'relation',
+ relation: {
+ source: { kind: 'Group', namespace: 'default', name: 'o' },
+ type: 'ownerOf',
+ target: { kind: 'System', namespace: 'default', name: 'n' },
+ },
+ });
+ expect(emit).toBeCalledWith({
+ type: 'relation',
+ relation: {
+ source: { kind: 'System', namespace: 'default', name: 'n' },
+ type: 'ownedBy',
+ target: { kind: 'Group', namespace: 'default', name: 'o' },
+ },
+ });
+ expect(emit).toBeCalledWith({
+ type: 'relation',
+ relation: {
+ source: { kind: 'Domain', namespace: 'default', name: 'd' },
+ type: 'hasPart',
+ target: { kind: 'System', namespace: 'default', name: 'n' },
+ },
+ });
+ expect(emit).toBeCalledWith({
+ type: 'relation',
+ relation: {
+ source: { kind: 'System', namespace: 'default', name: 'n' },
+ type: 'partOf',
+ target: { kind: 'Domain', namespace: 'default', name: 'd' },
+ },
+ });
+ });
+
+ it('generates relations for domain entities', async () => {
+ const entity: DomainEntity = {
+ apiVersion: 'backstage.io/v1alpha1',
+ kind: 'Domain',
+ metadata: { name: 'n' },
+ spec: {
+ owner: 'o',
+ },
+ };
+
+ await processor.postProcessEntity(entity, location, emit);
+
+ expect(emit).toBeCalledTimes(2);
+ expect(emit).toBeCalledWith({
+ type: 'relation',
+ relation: {
+ source: { kind: 'Group', namespace: 'default', name: 'o' },
+ type: 'ownerOf',
+ target: { kind: 'Domain', namespace: 'default', name: 'n' },
+ },
+ });
+ expect(emit).toBeCalledWith({
+ type: 'relation',
+ relation: {
+ source: { kind: 'Domain', namespace: 'default', name: 'n' },
+ type: 'ownedBy',
+ target: { kind: 'Group', namespace: 'default', name: 'o' },
+ },
+ });
});
it('generates relations for user entities', async () => {
diff --git a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts
index 67e89ac52c..c75a46874d 100644
--- a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts
+++ b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts
@@ -19,6 +19,8 @@ import {
apiEntityV1alpha1Validator,
ComponentEntity,
componentEntityV1alpha1Validator,
+ DomainEntity,
+ domainEntityV1alpha1Validator,
Entity,
getEntityName,
GroupEntity,
@@ -31,13 +33,17 @@ import {
RELATION_CHILD_OF,
RELATION_CONSUMES_API,
RELATION_HAS_MEMBER,
- RELATION_MEMBER_OF,
RELATION_HAS_PART,
- RELATION_PART_OF,
+ RELATION_MEMBER_OF,
RELATION_OWNED_BY,
RELATION_OWNER_OF,
RELATION_PARENT_OF,
+ RELATION_PART_OF,
RELATION_PROVIDES_API,
+ ResourceEntity,
+ resourceEntityV1alpha1Validator,
+ SystemEntity,
+ systemEntityV1alpha1Validator,
templateEntityV1alpha1Validator,
UserEntity,
userEntityV1alpha1Validator,
@@ -49,10 +55,13 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor {
private readonly validators = [
apiEntityV1alpha1Validator,
componentEntityV1alpha1Validator,
+ resourceEntityV1alpha1Validator,
groupEntityV1alpha1Validator,
locationEntityV1alpha1Validator,
templateEntityV1alpha1Validator,
userEntityV1alpha1Validator,
+ systemEntityV1alpha1Validator,
+ domainEntityV1alpha1Validator,
];
async validateEntityKind(entity: Entity): Promise {
@@ -135,6 +144,12 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor {
RELATION_CONSUMES_API,
RELATION_API_CONSUMED_BY,
);
+ doEmit(
+ component.spec.system,
+ { defaultKind: 'System', defaultNamespace: selfRef.namespace },
+ RELATION_PART_OF,
+ RELATION_HAS_PART,
+ );
}
/*
@@ -149,6 +164,32 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor {
RELATION_OWNED_BY,
RELATION_OWNER_OF,
);
+ doEmit(
+ api.spec.system,
+ { defaultKind: 'System', defaultNamespace: selfRef.namespace },
+ RELATION_PART_OF,
+ RELATION_HAS_PART,
+ );
+ }
+
+ /*
+ * Emit relations for the Resource kind
+ */
+
+ if (entity.kind === 'Resource') {
+ const resource = entity as ResourceEntity;
+ doEmit(
+ resource.spec.owner,
+ { defaultKind: 'Group', defaultNamespace: selfRef.namespace },
+ RELATION_OWNED_BY,
+ RELATION_OWNER_OF,
+ );
+ doEmit(
+ resource.spec.system,
+ { defaultKind: 'System', defaultNamespace: selfRef.namespace },
+ RELATION_PART_OF,
+ RELATION_HAS_PART,
+ );
}
/*
@@ -185,6 +226,40 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor {
);
}
+ /*
+ * Emit relations for the System kind
+ */
+
+ if (entity.kind === 'System') {
+ const system = entity as SystemEntity;
+ doEmit(
+ system.spec.owner,
+ { defaultKind: 'Group', defaultNamespace: selfRef.namespace },
+ RELATION_OWNED_BY,
+ RELATION_OWNER_OF,
+ );
+ doEmit(
+ system.spec.domain,
+ { defaultKind: 'Domain', defaultNamespace: selfRef.namespace },
+ RELATION_PART_OF,
+ RELATION_HAS_PART,
+ );
+ }
+
+ /*
+ * Emit relations for the Domain kind
+ */
+
+ if (entity.kind === 'Domain') {
+ const domain = entity as DomainEntity;
+ doEmit(
+ domain.spec.owner,
+ { defaultKind: 'Group', defaultNamespace: selfRef.namespace },
+ RELATION_OWNED_BY,
+ RELATION_OWNER_OF,
+ );
+ }
+
return entity;
}
}
From ede27abc4ab0b34b6c722621510a72ef4a062af6 Mon Sep 17 00:00:00 2001
From: Gabriel Peixoto
Date: Wed, 13 Jan 2021 00:35:53 -0300
Subject: [PATCH 22/92] fixed - string with different case
---
.changeset/gorgeous-poems-wash.md | 5 +++++
.../components/Cards/Group/MembersList/MembersListCard.tsx | 4 +++-
2 files changed, 8 insertions(+), 1 deletion(-)
create mode 100644 .changeset/gorgeous-poems-wash.md
diff --git a/.changeset/gorgeous-poems-wash.md b/.changeset/gorgeous-poems-wash.md
new file mode 100644
index 0000000000..602dca79fb
--- /dev/null
+++ b/.changeset/gorgeous-poems-wash.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-org': patch
+---
+
+Fixed - normalizing strings for comparison when ignoring when one is in low case.
diff --git a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx
index 579db9d9a5..04a137d515 100644
--- a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx
+++ b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx
@@ -125,7 +125,9 @@ export const MembersListCard = ({
UserEntity
>).filter(member =>
member?.relations?.some(
- r => r.type === RELATION_MEMBER_OF && r.target.name === groupName,
+ r =>
+ r.type === RELATION_MEMBER_OF &&
+ r.target.name.toLowerCase() === groupName.toLowerCase(),
),
);
return groupMembersList;
From 6dee39ebe285f4f4013fbafad683c5da0a57b68d Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Wed, 13 Jan 2021 09:16:58 +0100
Subject: [PATCH 23/92] Fix spelling.
---
docs/tutorials/quickstart-app-auth.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 205fcddb65..14ed856c96 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -103,7 +103,7 @@ auth:
-GitLab
+Gitlab
### 1. Open `app-config.yaml` and change it as follows
From e921fb2ec7428029748ad4d5a109a32ebde437ea Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Wed, 13 Jan 2021 09:25:25 +0100
Subject: [PATCH 24/92] Fix GitHub stylization
---
docs/tutorials/quickstart-app-auth.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 14ed856c96..4218940ef7 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -60,7 +60,7 @@ external authentication provider. Please see a subset of possible providers and
instructions to integrate them below. Steps 1 & 2 are described separately for
each provider and steps beyond that are common for all.
-Github
+GitHub
### 1. Open `app-config.yaml` and change it as follows
@@ -334,7 +334,7 @@ above:
| Auth Provider | Import Name |
| ------------- | ------------------- |
-| Github | githubAuthApiRef |
+| GitHub | githubAuthApiRef |
| Gitlab | gitlabAuthApiRef |
| Google | googleAuthApiRef |
| Microsoft | microsoftAuthApiRef |
From 37e4bf3473a1f399f572eb7bdcb636f57204ebd8 Mon Sep 17 00:00:00 2001
From: Jussi Hallila
Date: Wed, 13 Jan 2021 10:12:40 +0100
Subject: [PATCH 25/92] Modifying GitLab text to be stylized
---
docs/tutorials/quickstart-app-auth.md | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md
index 4218940ef7..1c661d274c 100644
--- a/docs/tutorials/quickstart-app-auth.md
+++ b/docs/tutorials/quickstart-app-auth.md
@@ -93,8 +93,8 @@ auth:
- Log into http://github.com
- Navigate to (Settings > Developer Settings > OAuth Apps > New OAuth
App)[https://github.com/settings/applications/new]
-- Set Homepage URL = http://localhost:3000
-- Set Callback URL = http://localhost:7000/api/auth/github
+- Set Homepage URL = `http://localhost:3000`
+- Set Callback URL = `http://localhost:7000/api/auth/github`
- Click [Register application]
- On the next page, copy and paste your new Client ID and Client Secret to
environment variables defined in the `app-config.yaml` file,
@@ -103,7 +103,7 @@ auth:
-Gitlab
+GitLab
### 1. Open `app-config.yaml` and change it as follows
@@ -126,16 +126,16 @@ auth:
$env: AUTH_GITLAB_CLIENT_ID
clientSecret:
$env: AUTH_GITLAB_CLIENT_SECRET
- audience: https://gitlab.com # Or your self-hosted Gitlab instance URL
+ audience: https://gitlab.com # Or your self-hosted GitLab instance URL
```
-### 2. Generate a Gitlab Application client ID and secret
+### 2. Generate a GitLab Application client ID and secret
- Log into GitLab
- Navigate to (Profile > Settings >
Applications)[https://gitlab.com/-/profile/applications]
- Name your application
-- Set Callback URL = http://localhost:7000/api/auth/gitlab/handler/frame
+- Set Callback URL = `http://localhost:7000/api/auth/gitlab/handler/frame`
- Select the following values:
- `read_user` (Read the authenticated user's personal information)
- `read_repository` (Allows read-only access to the repository)
@@ -335,7 +335,7 @@ above:
| Auth Provider | Import Name |
| ------------- | ------------------- |
| GitHub | githubAuthApiRef |
-| Gitlab | gitlabAuthApiRef |
+| GitLab | gitlabAuthApiRef |
| Google | googleAuthApiRef |
| Microsoft | microsoftAuthApiRef |
| Auth0 | auth0AuthApiRef |
From cc068c0d6f8a0b2859cf5b0acb2a180f9764814e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?=
Date: Tue, 12 Jan 2021 10:45:46 +0100
Subject: [PATCH 26/92] Bump gitbeaker to 28.x
---
.changeset/quiet-trainers-study.md | 12 ++++++++
.github/styles/vocab.txt | 1 +
packages/backend/package.json | 2 +-
.../packages/backend/package.json.hbs | 2 +-
plugins/scaffolder-backend/package.json | 4 +--
yarn.lock | 30 +++++++++----------
6 files changed, 32 insertions(+), 19 deletions(-)
create mode 100644 .changeset/quiet-trainers-study.md
diff --git a/.changeset/quiet-trainers-study.md b/.changeset/quiet-trainers-study.md
new file mode 100644
index 0000000000..2e2b8f3566
--- /dev/null
+++ b/.changeset/quiet-trainers-study.md
@@ -0,0 +1,12 @@
+---
+'example-backend': patch
+'@backstage/create-app': patch
+'@backstage/plugin-scaffolder-backend': patch
+---
+
+Bump the gitbeaker dependencies to 28.x.
+
+To update your own installation, go through the `package.json` files of all of
+your packages, and ensure that all dependencies on `@gitbeaker/node` or
+`@gitbeaker/core` are at version `^28.0.2`. Then run `yarn install` at the root
+of your repo.
diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt
index f4c7137d23..a79d27bcec 100644
--- a/.github/styles/vocab.txt
+++ b/.github/styles/vocab.txt
@@ -77,6 +77,7 @@ Firekube
Fiverr
freben
Fredrik
+gitbeaker
GitHub
GitLab
Grafana
diff --git a/packages/backend/package.json b/packages/backend/package.json
index b8f60bf13b..0ecb44bf0b 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -39,7 +39,7 @@
"@backstage/plugin-rollbar-backend": "^0.1.5",
"@backstage/plugin-scaffolder-backend": "^0.4.0",
"@backstage/plugin-techdocs-backend": "^0.5.0",
- "@gitbeaker/node": "^25.2.0",
+ "@gitbeaker/node": "^28.0.2",
"@octokit/rest": "^18.0.0",
"azure-devops-node-api": "^10.1.1",
"dockerode": "^3.2.1",
diff --git a/packages/create-app/templates/default-app/packages/backend/package.json.hbs b/packages/create-app/templates/default-app/packages/backend/package.json.hbs
index c62d7a5e02..d9e7726c06 100644
--- a/packages/create-app/templates/default-app/packages/backend/package.json.hbs
+++ b/packages/create-app/templates/default-app/packages/backend/package.json.hbs
@@ -28,7 +28,7 @@
"@backstage/plugin-scaffolder-backend": "^{{version '@backstage/plugin-scaffolder-backend'}}",
"@backstage/plugin-techdocs-backend": "^{{version '@backstage/plugin-techdocs-backend'}}",
"@octokit/rest": "^18.0.0",
- "@gitbeaker/node": "^25.2.0",
+ "@gitbeaker/node": "^28.0.2",
"dockerode": "^3.2.1",
"express": "^4.17.1",
"express-promise-router": "^3.0.3",
diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json
index c2cef2f73e..efbc73ff4b 100644
--- a/plugins/scaffolder-backend/package.json
+++ b/plugins/scaffolder-backend/package.json
@@ -33,8 +33,8 @@
"@backstage/catalog-model": "^0.6.0",
"@backstage/config": "^0.1.2",
"@backstage/integration": "^0.1.5",
- "@gitbeaker/core": "^25.2.0",
- "@gitbeaker/node": "^25.2.0",
+ "@gitbeaker/core": "^28.0.2",
+ "@gitbeaker/node": "^28.0.2",
"@octokit/rest": "^18.0.0",
"@types/dockerode": "^3.2.1",
"@types/express": "^4.17.6",
diff --git a/yarn.lock b/yarn.lock
index a0004872ad..150ed20f51 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2963,30 +2963,30 @@
dependencies:
yaml-ast-parser "0.0.43"
-"@gitbeaker/core@^25.2.0":
- version "25.6.0"
- resolved "https://registry.npmjs.org/@gitbeaker/core/-/core-25.6.0.tgz#97d5ccc5d61bab6b678bec280036d594d275931e"
- integrity sha512-+CohJNsbZiPl7jPgw7PHt5t0JIIV9NngObOskY1Ww8jef7SqaKpz0NsbSDawuWFBdmXApMpK81AEfASKtVI+cw==
+"@gitbeaker/core@^28.0.2":
+ version "28.0.2"
+ resolved "https://registry.npmjs.org/@gitbeaker/core/-/core-28.0.2.tgz#5c2cbe27a20426a96d9511642153ead18ff88877"
+ integrity sha512-H+0ChYSxLh+EVNzmOguHaJMR7xB9FlVZGi89JQ7BJ2meq0h1TQl+QaGqTyTNM4GYhn7cYrl7ebGoU0yrnYUWSg==
dependencies:
- "@gitbeaker/requester-utils" "^25.6.0"
+ "@gitbeaker/requester-utils" "^28.0.2"
form-data "^3.0.0"
li "^1.3.0"
xcase "^2.0.1"
-"@gitbeaker/node@^25.2.0":
- version "25.2.0"
- resolved "https://registry.npmjs.org/@gitbeaker/node/-/node-25.2.0.tgz#cc91e83328ec32de0b1a0dac23accd2385734a66"
- integrity sha512-FWchXYJ5agn0ptAQxtkkSKSg1ObbP2xfMzHLECxINFRBHYhg0ms8Fp8Qb+71pxJz7IMlvajyEtZaPfHBmyuh9Q==
+"@gitbeaker/node@^28.0.2":
+ version "28.0.2"
+ resolved "https://registry.npmjs.org/@gitbeaker/node/-/node-28.0.2.tgz#ead4eb9e1b4400d3a6fe9769b50e43e84fc99747"
+ integrity sha512-FCILxXiRep3PUpe/P2+Ak1KdwzIz3azxf1OY8Jb72gKjNNTgCi4ByFCIpHkvbFZlr7kDKTtcPyEN6oA6qnrCdQ==
dependencies:
- "@gitbeaker/core" "^25.2.0"
- "@gitbeaker/requester-utils" "^25.2.0"
+ "@gitbeaker/core" "^28.0.2"
+ "@gitbeaker/requester-utils" "^28.0.2"
got "^11.7.0"
xcase "^2.0.1"
-"@gitbeaker/requester-utils@^25.2.0", "@gitbeaker/requester-utils@^25.6.0":
- version "25.6.0"
- resolved "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-25.6.0.tgz#001a432a48460bb5196a02ed71763eb707a1a01e"
- integrity sha512-jD8cHbAZPR6+cB3HiukQxcqIKF5VkHtqg2m+Ns6ROE1pb0oRn10D/a9J1lZOXC9Jz2tQOBMWfHlplbmFFdB6Og==
+"@gitbeaker/requester-utils@^28.0.2":
+ version "28.0.2"
+ resolved "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-28.0.2.tgz#3c1bf97e6b73053ef3c38a2871cbc8e1a2e7501a"
+ integrity sha512-S851vQi0x9Sg5Tx0BcwzkG6OTIG0X4qYTBaA9kPM2sEKg2OOsd8X0HRhj/TbFGr/YVz+3Ubu7oueeWSyr5jvfQ==
dependencies:
form-data "^3.0.0"
query-string "^6.13.3"
From 371f67ecd0473fee596d5832fc8931371c5e164e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?=
Date: Wed, 13 Jan 2021 10:37:02 +0100
Subject: [PATCH 27/92] techdocs-common: fix to-string breakage of binary files
---
.changeset/funny-snails-cry.md | 5 +++++
packages/techdocs-common/src/stages/publish/awsS3.ts | 10 +++++-----
2 files changed, 10 insertions(+), 5 deletions(-)
create mode 100644 .changeset/funny-snails-cry.md
diff --git a/.changeset/funny-snails-cry.md b/.changeset/funny-snails-cry.md
new file mode 100644
index 0000000000..161a386a8c
--- /dev/null
+++ b/.changeset/funny-snails-cry.md
@@ -0,0 +1,5 @@
+---
+'@backstage/techdocs-common': patch
+---
+
+fix to-string breakage of binary files
diff --git a/packages/techdocs-common/src/stages/publish/awsS3.ts b/packages/techdocs-common/src/stages/publish/awsS3.ts
index 7a21ae6475..3f7a0e3d81 100644
--- a/packages/techdocs-common/src/stages/publish/awsS3.ts
+++ b/packages/techdocs-common/src/stages/publish/awsS3.ts
@@ -24,13 +24,13 @@ import { PublisherBase, PublishRequest } from './types';
import fs from 'fs-extra';
import { Readable } from 'stream';
-const streamToString = (stream: Readable): Promise => {
+const streamToBuffer = (stream: Readable): Promise => {
return new Promise((resolve, reject) => {
try {
const chunks: any[] = [];
stream.on('data', chunk => chunks.push(chunk));
stream.on('error', reject);
- stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
+ stream.on('end', () => resolve(Buffer.concat(chunks)));
} catch (e) {
throw new Error(`Unable to parse the response data, ${e.message}`);
}
@@ -173,7 +173,7 @@ export class AwsS3Publish implements PublisherBase {
Key: `${entityRootDir}/techdocs_metadata.json`,
})
.then(async file => {
- const techdocsMetadataJson = await streamToString(
+ const techdocsMetadataJson = await streamToBuffer(
file.Body as Readable,
);
@@ -183,7 +183,7 @@ export class AwsS3Publish implements PublisherBase {
);
}
- resolve(techdocsMetadataJson);
+ resolve(techdocsMetadataJson.toString('utf-8'));
})
.catch(err => {
this.logger.error(err.message);
@@ -211,7 +211,7 @@ export class AwsS3Publish implements PublisherBase {
this.storageClient
.getObject({ Bucket: this.bucketName, Key: filePath })
.then(async object => {
- const fileContent = await streamToString(object.Body as Readable);
+ const fileContent = await streamToBuffer(object.Body as Readable);
if (!fileContent) {
throw new Error(`Unable to parse the file ${filePath}.`);
}
From 30cc3e44ba3f9dfddc87c7e8cfc3610b37fffc72 Mon Sep 17 00:00:00 2001
From: jmfrancois
Date: Wed, 13 Jan 2021 10:45:31 +0100
Subject: [PATCH 28/92] chore: upgrade passport-saml to fix security issue
---
plugins/auth-backend/config.d.ts | 2 +-
plugins/auth-backend/package.json | 2 +-
yarn.lock | 25 ++++++++++++-------------
3 files changed, 14 insertions(+), 15 deletions(-)
diff --git a/plugins/auth-backend/config.d.ts b/plugins/auth-backend/config.d.ts
index 7c105af524..c748090711 100644
--- a/plugins/auth-backend/config.d.ts
+++ b/plugins/auth-backend/config.d.ts
@@ -51,7 +51,7 @@ export interface Config {
cert?: string;
privateKey?: string;
decryptionPvk?: string;
- signatureAlgorithm?: 'sha1' | 'sha256' | 'sha512';
+ signatureAlgorithm?: 'sha256' | 'sha512';
digestAlgorithm?: string;
};
okta?: {
diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json
index 713ad07617..fa1ef30695 100644
--- a/plugins/auth-backend/package.json
+++ b/plugins/auth-backend/package.json
@@ -58,7 +58,7 @@
"passport-oauth2": "^1.5.0",
"passport-okta-oauth": "^0.0.1",
"passport-onelogin-oauth": "^0.0.1",
- "passport-saml": "^1.3.5, <1.4.0",
+ "passport-saml": "^2.0.0",
"uuid": "^8.0.0",
"winston": "^3.2.1",
"yn": "^4.0.0"
diff --git a/yarn.lock b/yarn.lock
index a0004872ad..b824faa034 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -19879,17 +19879,16 @@ passport-onelogin-oauth@^0.0.1:
pkginfo "0.2.x"
uid2 "0.0.3"
-"passport-saml@^1.3.5, <1.4.0":
- version "1.3.5"
- resolved "https://registry.npmjs.org/passport-saml/-/passport-saml-1.3.5.tgz#747f2c8bb8b9fed41e8cd14586df5aa83e8a8996"
- integrity sha512-HFamiqgGiMRCbUBm3wx02WYWKb6ojke0WJHrg4QXI8tx35HrTmDiY8MksUXhouJtEkfcRwXjBL2cSEpRQ6+PgQ==
+passport-saml@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.npmjs.org/passport-saml/-/passport-saml-2.0.2.tgz#41c459f0f4c4cf0374b161bf3f9ab008fcacb8fe"
+ integrity sha512-e8rT/nWEY3YalktzZlGXvnj6mLDQD4R+atfStCF4nHCbqK5IUpip+Bj2Wzbm9v5F/KI497k57kNXkHCLfDSVjA==
dependencies:
debug "^3.1.0"
passport-strategy "*"
- q "^1.5.0"
- xml-crypto "^1.4.0"
+ xml-crypto "^2.0.0"
xml-encryption "1.2.1"
- xml2js "0.4.x"
+ xml2js "^0.4.23"
xmlbuilder "^11.0.0"
xmldom "0.1.x"
@@ -21043,7 +21042,7 @@ pupa@^2.0.1:
dependencies:
escape-goat "^2.0.0"
-q@^1.1.2, q@^1.5.0, q@^1.5.1:
+q@^1.1.2, q@^1.5.1:
version "1.5.1"
resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
@@ -26074,10 +26073,10 @@ xml-but-prettier@^1.0.1:
dependencies:
repeat-string "^1.5.2"
-xml-crypto@^1.4.0:
- version "1.5.3"
- resolved "https://registry.npmjs.org/xml-crypto/-/xml-crypto-1.5.3.tgz#a8f500b90f0dfaf0efa3331c345ecb0fff993c34"
- integrity sha512-uHkmpUtX15xExe5iimPmakAZN+6CqIvjmaJTy4FwqGzaTjrKRBNeqMh8zGEzVNgW0dk6beFYpyQSgqV/J6C5xA==
+xml-crypto@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmjs.org/xml-crypto/-/xml-crypto-2.0.0.tgz#54cd268ad9d31930afcf7092cbb664258ca9e826"
+ integrity sha512-/a04qr7RpONRZHOxROZ6iIHItdsQQjN3sj8lJkYDDss8tAkEaAs0VrFjb3tlhmS5snQru5lTs9/5ISSMdPDHlg==
dependencies:
xmldom "0.1.27"
xpath "0.0.27"
@@ -26097,7 +26096,7 @@ xml-name-validator@^3.0.0:
resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
-xml2js@0.4.x:
+xml2js@^0.4.23:
version "0.4.23"
resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
From 0f7710b9106f374468fc5ecc59925504c269981d Mon Sep 17 00:00:00 2001
From: jmfrancois
Date: Wed, 13 Jan 2021 11:54:44 +0100
Subject: [PATCH 29/92] fix: CI
---
plugins/auth-backend/package.json | 2 +
.../src/providers/saml/provider.ts | 2 +-
yarn.lock | 38 ++++++++++++++-----
3 files changed, 32 insertions(+), 10 deletions(-)
diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json
index fa1ef30695..38d074f7fb 100644
--- a/plugins/auth-backend/package.json
+++ b/plugins/auth-backend/package.json
@@ -74,6 +74,8 @@
"@types/passport-google-oauth20": "^2.0.3",
"@types/passport-microsoft": "^0.0.0",
"@types/passport-saml": "^1.1.2",
+ "@types/passport-strategy": "^0.2.35",
+ "@types/xml2js": "^0.4.7",
"msw": "^0.21.2",
"nock": "^13.0.5"
},
diff --git a/plugins/auth-backend/src/providers/saml/provider.ts b/plugins/auth-backend/src/providers/saml/provider.ts
index 45676ac43e..74541d8294 100644
--- a/plugins/auth-backend/src/providers/saml/provider.ts
+++ b/plugins/auth-backend/src/providers/saml/provider.ts
@@ -15,8 +15,8 @@
*/
import express from 'express';
+import { SamlConfig } from 'passport-saml/lib/passport-saml/types';
import {
- SamlConfig,
Strategy as SamlStrategy,
Profile as SamlProfile,
VerifyWithoutRequest,
diff --git a/yarn.lock b/yarn.lock
index b824faa034..15913d0c49 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2436,9 +2436,11 @@
to-fast-properties "^2.0.0"
"@backstage/catalog-model@^0.2.0":
- version "0.6.0"
+ version "0.2.0"
+ resolved "https://registry.npmjs.org/@backstage/catalog-model/-/catalog-model-0.2.0.tgz#e3fe2a4ddeb6a9b6ec480c80cb2b9c39cb245576"
+ integrity sha512-Y1ocdRpBlxK/VrJQjHlQd0bgADECd1B2NRjwd8ss46ibT5hwLvMOfD80+Fa7oPLu0ktJrH4lq0pNIIJIml48zA==
dependencies:
- "@backstage/config" "^0.1.2"
+ "@backstage/config" "^0.1.1"
"@types/json-schema" "^7.0.5"
"@types/yup" "^0.29.8"
json-schema "^0.2.5"
@@ -2447,9 +2449,11 @@
yup "^0.29.3"
"@backstage/catalog-model@^0.3.0":
- version "0.6.0"
+ version "0.3.1"
+ resolved "https://registry.npmjs.org/@backstage/catalog-model/-/catalog-model-0.3.1.tgz#45d08e2f333c9c566b2bf2629fd707fe989bb404"
+ integrity sha512-9XhV7c4rmVW+Yzj2PiwTQ7DsegWGB3C4ELsDRExuEVZONdqNcC02cyJtrt3fT5F31ZS3tHkB9bMUymFOBLqUSA==
dependencies:
- "@backstage/config" "^0.1.2"
+ "@backstage/config" "^0.1.1"
"@types/json-schema" "^7.0.5"
"@types/yup" "^0.29.8"
json-schema "^0.2.5"
@@ -2458,16 +2462,17 @@
yup "^0.29.3"
"@backstage/core@^0.3.0":
- version "0.4.3"
+ version "0.3.2"
+ resolved "https://registry.npmjs.org/@backstage/core/-/core-0.3.2.tgz#a8209126d5076cf4a8b9bd632fe4e5e2edb62916"
+ integrity sha512-i5d+Wh8js4qEWoAsPY5L7HVSWpumr1OhfF2dUCGYdyW6AMqVJPca6+n6zp1Rg2CO+J9norp44XAVVCbyhtUpig==
dependencies:
- "@backstage/config" "^0.1.2"
- "@backstage/core-api" "^0.2.8"
- "@backstage/theme" "^0.2.2"
+ "@backstage/config" "^0.1.1"
+ "@backstage/core-api" "^0.2.1"
+ "@backstage/theme" "^0.2.1"
"@material-ui/core" "^4.11.0"
"@material-ui/icons" "^4.9.1"
"@material-ui/lab" "4.0.0-alpha.45"
"@types/dagre" "^0.7.44"
- "@types/prop-types" "^15.7.3"
"@types/react" "^16.9"
"@types/react-sparklines" "^1.7.0"
classnames "^2.2.6"
@@ -6821,6 +6826,14 @@
"@types/express" "*"
"@types/passport" "*"
+"@types/passport-strategy@^0.2.35":
+ version "0.2.35"
+ resolved "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz#e52f5212279ea73f02d9b06af67efe9cefce2d0c"
+ integrity sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==
+ dependencies:
+ "@types/express" "*"
+ "@types/passport" "*"
+
"@types/passport@*", "@types/passport@^1.0.3":
version "1.0.4"
resolved "https://registry.npmjs.org/@types/passport/-/passport-1.0.4.tgz#1b35c4e197560d3974fa5f71711b6e9cce0711f0"
@@ -7275,6 +7288,13 @@
dependencies:
"@types/node" "*"
+"@types/xml2js@^0.4.7":
+ version "0.4.7"
+ resolved "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.7.tgz#cd5b6c67bbec741ac625718a76e6cb99bc34365e"
+ integrity sha512-f5VOKSMEE0O+/L54FHwA/a7vcx9mHeSDM71844yHCOhh8Cin2xQa0UFw0b7Vc5hoZ3Ih6ZHaDobjfLih4tWPNw==
+ dependencies:
+ "@types/node" "*"
+
"@types/yargs-parser@*":
version "15.0.0"
resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
From f573cf36874dfdd5c29ed7ea466477c5ad4dfd90 Mon Sep 17 00:00:00 2001
From: Gabriel Peixoto
Date: Wed, 13 Jan 2021 00:35:53 -0300
Subject: [PATCH 30/92] fixed - string with different case
---
.changeset/gorgeous-poems-wash.md | 5 +++++
.../components/Cards/Group/MembersList/MembersListCard.tsx | 4 +++-
2 files changed, 8 insertions(+), 1 deletion(-)
create mode 100644 .changeset/gorgeous-poems-wash.md
diff --git a/.changeset/gorgeous-poems-wash.md b/.changeset/gorgeous-poems-wash.md
new file mode 100644
index 0000000000..602dca79fb
--- /dev/null
+++ b/.changeset/gorgeous-poems-wash.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-org': patch
+---
+
+Fixed - normalizing strings for comparison when ignoring when one is in low case.
diff --git a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx
index 579db9d9a5..04a137d515 100644
--- a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx
+++ b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx
@@ -125,7 +125,9 @@ export const MembersListCard = ({
UserEntity
>).filter(member =>
member?.relations?.some(
- r => r.type === RELATION_MEMBER_OF && r.target.name === groupName,
+ r =>
+ r.type === RELATION_MEMBER_OF &&
+ r.target.name.toLowerCase() === groupName.toLowerCase(),
),
);
return groupMembersList;
From f2de73ae06a0d78d83525b4e034989f9c95e4a72 Mon Sep 17 00:00:00 2001
From: jmfrancois
Date: Wed, 13 Jan 2021 14:21:50 +0100
Subject: [PATCH 31/92] chore: yarn install to update the lock file
---
yarn.lock | 23 +++++++++--------------
1 file changed, 9 insertions(+), 14 deletions(-)
diff --git a/yarn.lock b/yarn.lock
index 15913d0c49..1c14ea766e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2436,11 +2436,9 @@
to-fast-properties "^2.0.0"
"@backstage/catalog-model@^0.2.0":
- version "0.2.0"
- resolved "https://registry.npmjs.org/@backstage/catalog-model/-/catalog-model-0.2.0.tgz#e3fe2a4ddeb6a9b6ec480c80cb2b9c39cb245576"
- integrity sha512-Y1ocdRpBlxK/VrJQjHlQd0bgADECd1B2NRjwd8ss46ibT5hwLvMOfD80+Fa7oPLu0ktJrH4lq0pNIIJIml48zA==
+ version "0.6.0"
dependencies:
- "@backstage/config" "^0.1.1"
+ "@backstage/config" "^0.1.2"
"@types/json-schema" "^7.0.5"
"@types/yup" "^0.29.8"
json-schema "^0.2.5"
@@ -2449,11 +2447,9 @@
yup "^0.29.3"
"@backstage/catalog-model@^0.3.0":
- version "0.3.1"
- resolved "https://registry.npmjs.org/@backstage/catalog-model/-/catalog-model-0.3.1.tgz#45d08e2f333c9c566b2bf2629fd707fe989bb404"
- integrity sha512-9XhV7c4rmVW+Yzj2PiwTQ7DsegWGB3C4ELsDRExuEVZONdqNcC02cyJtrt3fT5F31ZS3tHkB9bMUymFOBLqUSA==
+ version "0.6.0"
dependencies:
- "@backstage/config" "^0.1.1"
+ "@backstage/config" "^0.1.2"
"@types/json-schema" "^7.0.5"
"@types/yup" "^0.29.8"
json-schema "^0.2.5"
@@ -2462,17 +2458,16 @@
yup "^0.29.3"
"@backstage/core@^0.3.0":
- version "0.3.2"
- resolved "https://registry.npmjs.org/@backstage/core/-/core-0.3.2.tgz#a8209126d5076cf4a8b9bd632fe4e5e2edb62916"
- integrity sha512-i5d+Wh8js4qEWoAsPY5L7HVSWpumr1OhfF2dUCGYdyW6AMqVJPca6+n6zp1Rg2CO+J9norp44XAVVCbyhtUpig==
+ version "0.4.3"
dependencies:
- "@backstage/config" "^0.1.1"
- "@backstage/core-api" "^0.2.1"
- "@backstage/theme" "^0.2.1"
+ "@backstage/config" "^0.1.2"
+ "@backstage/core-api" "^0.2.8"
+ "@backstage/theme" "^0.2.2"
"@material-ui/core" "^4.11.0"
"@material-ui/icons" "^4.9.1"
"@material-ui/lab" "4.0.0-alpha.45"
"@types/dagre" "^0.7.44"
+ "@types/prop-types" "^15.7.3"
"@types/react" "^16.9"
"@types/react-sparklines" "^1.7.0"
classnames "^2.2.6"
From 1caa021e94b9b7611c0949b17ae6ebd529659e32 Mon Sep 17 00:00:00 2001
From: Patrik Oldsberg
Date: Wed, 13 Jan 2021 14:32:28 +0100
Subject: [PATCH 32/92] codeowners: add changeset prefix for techdocs
---
.github/CODEOWNERS | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 495f1ff78d..11a756a597 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -13,3 +13,4 @@
/plugins/techdocs-backend @backstage/techdocs-core
/packages/techdocs-common @backstage/techdocs-core
/.changeset/cost-insights-* @backstage/silver-lining
+/.changeset/techdocs-* @backstage/techdocs-core
From 08e9893d2683f8896272e9d3716aecf6e627554e Mon Sep 17 00:00:00 2001
From: ahrberg
Date: Mon, 11 Jan 2021 16:51:14 +0100
Subject: [PATCH 33/92] cli: Handle no npm info
---
.changeset/quote-plastic-monk.md | 5 ++
.../cli/src/commands/versions/bump.test.ts | 67 +++++++++++++++++++
packages/cli/src/commands/versions/bump.ts | 23 ++++++-
packages/cli/src/lib/errors.ts | 2 +
.../cli/src/lib/versioning/packages.test.ts | 9 +++
packages/cli/src/lib/versioning/packages.ts | 6 ++
6 files changed, 110 insertions(+), 2 deletions(-)
create mode 100644 .changeset/quote-plastic-monk.md
diff --git a/.changeset/quote-plastic-monk.md b/.changeset/quote-plastic-monk.md
new file mode 100644
index 0000000000..231afa33e7
--- /dev/null
+++ b/.changeset/quote-plastic-monk.md
@@ -0,0 +1,5 @@
+---
+'@backstage/cli': patch
+---
+
+Handle no npm info
diff --git a/packages/cli/src/commands/versions/bump.test.ts b/packages/cli/src/commands/versions/bump.test.ts
index 164a202385..987d4f6f31 100644
--- a/packages/cli/src/commands/versions/bump.test.ts
+++ b/packages/cli/src/commands/versions/bump.test.ts
@@ -177,4 +177,71 @@ describe('bump', () => {
},
});
});
+
+ it('should ignore not found packages', async () => {
+ // Make sure all modules involved in package discovery are in the module cache before we mock fs
+ await mapDependencies(paths.targetDir);
+ mockFs({
+ '/yarn.lock': lockfileMockResult,
+ '/lerna.json': JSON.stringify({
+ packages: ['packages/*'],
+ }),
+ '/packages/a/package.json': JSON.stringify({
+ name: 'a',
+ dependencies: {
+ '@backstage/core': '^1.0.5',
+ },
+ }),
+ '/packages/b/package.json': JSON.stringify({
+ name: 'b',
+ dependencies: {
+ '@backstage/core': '^1.0.3',
+ '@backstage/theme': '^2.0.0',
+ },
+ }),
+ });
+
+ paths.targetDir = '/';
+ jest
+ .spyOn(paths, 'resolveTargetRoot')
+ .mockImplementation((...paths) => resolvePath('/', ...paths));
+ jest.spyOn(runObj, 'runPlain').mockImplementation(async () => '');
+ jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
+
+ const { log: logs } = await withLogCollector(['log'], async () => {
+ await bump();
+ });
+ expect(logs.filter(Boolean)).toEqual([
+ 'Checking for updates of @backstage/theme',
+ 'Checking for updates of @backstage/core',
+ 'Package info not found, ignoring package @backstage/theme',
+ 'Package info not found, ignoring package @backstage/core',
+ 'Checking for updates of @backstage/theme',
+ 'Checking for updates of @backstage/core',
+ 'Package info not found, ignoring package @backstage/theme',
+ 'Package info not found, ignoring package @backstage/core',
+ 'All Backstage packages are up to date!',
+ ]);
+
+ expect(runObj.run).toHaveBeenCalledTimes(0);
+
+ const lockfileContents = await fs.readFile('/yarn.lock', 'utf8');
+ expect(lockfileContents).toBe(lockfileMockResult);
+
+ const packageA = await fs.readJson('/packages/a/package.json');
+ expect(packageA).toEqual({
+ name: 'a',
+ dependencies: {
+ '@backstage/core': '^1.0.5', // not bumped
+ },
+ });
+ const packageB = await fs.readJson('/packages/b/package.json');
+ expect(packageB).toEqual({
+ name: 'b',
+ dependencies: {
+ '@backstage/core': '^1.0.3', // not bumped
+ '@backstage/theme': '^2.0.0', // not bumped
+ },
+ });
+ });
});
diff --git a/packages/cli/src/commands/versions/bump.ts b/packages/cli/src/commands/versions/bump.ts
index 8b32cd5768..af670d4a8b 100644
--- a/packages/cli/src/commands/versions/bump.ts
+++ b/packages/cli/src/commands/versions/bump.ts
@@ -26,6 +26,7 @@ import {
Lockfile,
} from '../../lib/versioning';
import { includedFilter, forbiddenDuplicatesFilter } from './lint';
+import { NotFoundError } from '../../lib/errors';
const DEP_TYPES = [
'dependencies',
@@ -55,7 +56,16 @@ export default async () => {
// Track package versions that we want to remove from yarn.lock in order to trigger a bump
const unlocked = Array<{ name: string; range: string; target: string }>();
await workerThreads(16, dependencyMap.entries(), async ([name, pkgs]) => {
- const target = await findTargetVersion(name);
+ let target: string;
+ try {
+ target = await findTargetVersion(name);
+ } catch (error) {
+ if (error instanceof NotFoundError) {
+ console.log(`Package info not found, ignoring package ${name}`);
+ return;
+ }
+ throw error;
+ }
for (const pkg of pkgs) {
if (semver.satisfies(target, pkg.range)) {
@@ -84,7 +94,16 @@ export default async () => {
return;
}
- const target = await findTargetVersion(name);
+ let target: string;
+ try {
+ target = await findTargetVersion(name);
+ } catch (error) {
+ if (error instanceof NotFoundError) {
+ console.log(`Package info not found, ignoring package ${name}`);
+ return;
+ }
+ throw error;
+ }
for (const entry of lockfile.get(name) ?? []) {
// Ignore lockfile entries that don't satisfy the version range, since
diff --git a/packages/cli/src/lib/errors.ts b/packages/cli/src/lib/errors.ts
index a1eab4c9e5..110a095fe3 100644
--- a/packages/cli/src/lib/errors.ts
+++ b/packages/cli/src/lib/errors.ts
@@ -44,3 +44,5 @@ export function exitWithError(error: Error): never {
process.exit(1);
}
}
+
+export class NotFoundError extends CustomError {}
diff --git a/packages/cli/src/lib/versioning/packages.test.ts b/packages/cli/src/lib/versioning/packages.test.ts
index b0f1c46e8a..784be60632 100644
--- a/packages/cli/src/lib/versioning/packages.test.ts
+++ b/packages/cli/src/lib/versioning/packages.test.ts
@@ -19,6 +19,7 @@ import path from 'path';
import * as runObj from '../run';
import { paths } from '../paths';
import { fetchPackageInfo, mapDependencies } from './packages';
+import { NotFoundError } from '@backstage/backend-common';
describe('fetchPackageInfo', () => {
afterEach(() => {
@@ -40,6 +41,14 @@ describe('fetchPackageInfo', () => {
'my-package',
);
});
+
+ it('should throw if no info', async () => {
+ jest.spyOn(runObj, 'runPlain').mockResolvedValue('');
+
+ await expect(fetchPackageInfo('my-package')).rejects.toThrow(
+ new NotFoundError(`No package information found for package my-package`),
+ );
+ });
});
describe('mapDependencies', () => {
diff --git a/packages/cli/src/lib/versioning/packages.ts b/packages/cli/src/lib/versioning/packages.ts
index 76e5dc49b0..991a4d23ef 100644
--- a/packages/cli/src/lib/versioning/packages.ts
+++ b/packages/cli/src/lib/versioning/packages.ts
@@ -15,6 +15,7 @@
*/
import { runPlain } from '../../lib/run';
+import { NotFoundError } from '../errors';
const PREFIX = '@backstage';
@@ -49,6 +50,11 @@ export async function fetchPackageInfo(
name: string,
): Promise {
const output = await runPlain('yarn', 'info', '--json', name);
+
+ if (!output) {
+ throw new NotFoundError(`No package information found for package ${name}`);
+ }
+
const info = JSON.parse(output) as YarnInfo;
if (info.type !== 'inspect') {
throw new Error(`Received unknown yarn info for ${name}, ${output}`);
From b519606f3ae0641c90a5cb226ab96adc18731af5 Mon Sep 17 00:00:00 2001
From: ahrberg
Date: Wed, 13 Jan 2021 08:24:45 +0100
Subject: [PATCH 34/92] cli: review feedback
---
packages/cli/src/commands/versions/bump.ts | 2 +-
packages/cli/src/lib/versioning/packages.test.ts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/cli/src/commands/versions/bump.ts b/packages/cli/src/commands/versions/bump.ts
index af670d4a8b..753c5d5b57 100644
--- a/packages/cli/src/commands/versions/bump.ts
+++ b/packages/cli/src/commands/versions/bump.ts
@@ -98,7 +98,7 @@ export default async () => {
try {
target = await findTargetVersion(name);
} catch (error) {
- if (error instanceof NotFoundError) {
+ if (error.name === 'NotFoundError') {
console.log(`Package info not found, ignoring package ${name}`);
return;
}
diff --git a/packages/cli/src/lib/versioning/packages.test.ts b/packages/cli/src/lib/versioning/packages.test.ts
index 784be60632..5af040809a 100644
--- a/packages/cli/src/lib/versioning/packages.test.ts
+++ b/packages/cli/src/lib/versioning/packages.test.ts
@@ -19,7 +19,7 @@ import path from 'path';
import * as runObj from '../run';
import { paths } from '../paths';
import { fetchPackageInfo, mapDependencies } from './packages';
-import { NotFoundError } from '@backstage/backend-common';
+import { NotFoundError } from '../errors';
describe('fetchPackageInfo', () => {
afterEach(() => {
From f5b0d234315cc0f4ab38d0b17443d69650c21528 Mon Sep 17 00:00:00 2001
From: ahrberg
Date: Wed, 13 Jan 2021 15:48:57 +0100
Subject: [PATCH 35/92] cli: review feedback
---
packages/cli/src/commands/versions/bump.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/packages/cli/src/commands/versions/bump.ts b/packages/cli/src/commands/versions/bump.ts
index 753c5d5b57..ceb2f7f988 100644
--- a/packages/cli/src/commands/versions/bump.ts
+++ b/packages/cli/src/commands/versions/bump.ts
@@ -26,7 +26,6 @@ import {
Lockfile,
} from '../../lib/versioning';
import { includedFilter, forbiddenDuplicatesFilter } from './lint';
-import { NotFoundError } from '../../lib/errors';
const DEP_TYPES = [
'dependencies',
@@ -60,7 +59,7 @@ export default async () => {
try {
target = await findTargetVersion(name);
} catch (error) {
- if (error instanceof NotFoundError) {
+ if (error.name === 'NotFoundError') {
console.log(`Package info not found, ignoring package ${name}`);
return;
}
From 94fdf49554d69440af02d6f6e0c8639dc2f3c8fe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?=
Date: Tue, 12 Jan 2021 09:44:54 +0100
Subject: [PATCH 36/92] Get rid of all usages of @octokit/types, and bump the
rest of the octokit dependencies to the latest version
---
.changeset/great-vans-happen.md | 11 ++
.github/styles/vocab.txt | 1 +
package.json | 2 +-
packages/app/package.json | 2 +-
packages/backend/package.json | 2 +-
packages/cli/package.json | 2 +-
.../packages/backend/package.json.hbs | 2 +-
plugins/catalog-backend/package.json | 2 +-
plugins/catalog-import/package.json | 2 +-
plugins/cloudbuild/package.json | 2 -
plugins/github-actions/package.json | 3 +-
.../src/api/GithubActionsApi.ts | 23 ++--
.../src/api/GithubActionsClient.ts | 24 ++--
.../WorkflowRunDetails/WorkflowRunDetails.tsx | 6 +-
.../src/components/useWorkflowRuns.ts | 71 ++++++------
plugins/scaffolder-backend/package.json | 3 +-
.../scaffolder/stages/publish/github.test.ts | 29 ++---
yarn.lock | 103 +++++++++++++-----
18 files changed, 172 insertions(+), 118 deletions(-)
create mode 100644 .changeset/great-vans-happen.md
diff --git a/.changeset/great-vans-happen.md b/.changeset/great-vans-happen.md
new file mode 100644
index 0000000000..45d6eb2ee9
--- /dev/null
+++ b/.changeset/great-vans-happen.md
@@ -0,0 +1,11 @@
+---
+'@backstage/cli': patch
+'@backstage/create-app': patch
+'@backstage/plugin-catalog-backend': patch
+'@backstage/plugin-catalog-import': patch
+'@backstage/plugin-cloudbuild': patch
+'@backstage/plugin-github-actions': patch
+'@backstage/plugin-scaffolder-backend': patch
+---
+
+Get rid of all usages of @octokit/types, and bump the rest of the octokit dependencies to the latest version
diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt
index a79d27bcec..a71887f796 100644
--- a/.github/styles/vocab.txt
+++ b/.github/styles/vocab.txt
@@ -143,6 +143,7 @@ npm
nvarchar
nvm
OAuth
+octokit
oidc
Okta
Oldsberg
diff --git a/package.json b/package.json
index 8376915729..e2e3f1b075 100644
--- a/package.json
+++ b/package.json
@@ -43,7 +43,7 @@
"version": "1.0.0",
"devDependencies": {
"@changesets/cli": "^2.11.0",
- "@octokit/openapi-types": "^2.0.0",
+ "@octokit/openapi-types": "^2.2.0",
"@spotify/eslint-config-oss": "^1.0.1",
"@spotify/prettier-config": "^9.0.0",
"command-exists": "^1.2.9",
diff --git a/packages/app/package.json b/packages/app/package.json
index faa37a2378..e83af5a0c6 100644
--- a/packages/app/package.json
+++ b/packages/app/package.json
@@ -36,7 +36,7 @@
"@backstage/theme": "^0.2.2",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
- "@octokit/rest": "^18.0.0",
+ "@octokit/rest": "^18.0.12",
"@roadiehq/backstage-plugin-buildkite": "^0.1.3",
"@roadiehq/backstage-plugin-github-insights": "^0.2.16",
"@roadiehq/backstage-plugin-github-pull-requests": "^0.6.3",
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 0ecb44bf0b..1e6819b7b8 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -40,7 +40,7 @@
"@backstage/plugin-scaffolder-backend": "^0.4.0",
"@backstage/plugin-techdocs-backend": "^0.5.0",
"@gitbeaker/node": "^28.0.2",
- "@octokit/rest": "^18.0.0",
+ "@octokit/rest": "^18.0.12",
"azure-devops-node-api": "^10.1.1",
"dockerode": "^3.2.1",
"example-app": "^0.2.8",
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 277f710b5e..f6512b6261 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -34,7 +34,7 @@
"@hot-loader/react-dom": "^16.13.0",
"@lerna/package-graph": "^3.18.5",
"@lerna/project": "^3.18.0",
- "@octokit/request": "^5.2.0",
+ "@octokit/request": "^5.4.12",
"@rollup/plugin-commonjs": "^16.0.0",
"@rollup/plugin-json": "^4.0.2",
"@rollup/plugin-node-resolve": "^9.0.0",
diff --git a/packages/create-app/templates/default-app/packages/backend/package.json.hbs b/packages/create-app/templates/default-app/packages/backend/package.json.hbs
index d9e7726c06..7b7429d838 100644
--- a/packages/create-app/templates/default-app/packages/backend/package.json.hbs
+++ b/packages/create-app/templates/default-app/packages/backend/package.json.hbs
@@ -27,8 +27,8 @@
"@backstage/plugin-proxy-backend": "^{{version '@backstage/plugin-proxy-backend'}}",
"@backstage/plugin-scaffolder-backend": "^{{version '@backstage/plugin-scaffolder-backend'}}",
"@backstage/plugin-techdocs-backend": "^{{version '@backstage/plugin-techdocs-backend'}}",
- "@octokit/rest": "^18.0.0",
"@gitbeaker/node": "^28.0.2",
+ "@octokit/rest": "^18.0.12",
"dockerode": "^3.2.1",
"express": "^4.17.1",
"express-promise-router": "^3.0.3",
diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json
index 8fc9712777..9a782ac1e5 100644
--- a/plugins/catalog-backend/package.json
+++ b/plugins/catalog-backend/package.json
@@ -34,7 +34,7 @@
"@backstage/backend-common": "^0.4.2",
"@backstage/catalog-model": "^0.6.0",
"@backstage/config": "^0.1.2",
- "@octokit/graphql": "^4.5.6",
+ "@octokit/graphql": "^4.5.8",
"@types/express": "^4.17.6",
"@types/ldapjs": "^1.0.9",
"codeowners-utils": "^1.0.2",
diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json
index d308bef7b8..1a7d22d551 100644
--- a/plugins/catalog-import/package.json
+++ b/plugins/catalog-import/package.json
@@ -38,7 +38,7 @@
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
- "@octokit/rest": "^18.0.6",
+ "@octokit/rest": "^18.0.12",
"git-url-parse": "^11.4.3",
"react": "^16.13.1",
"react-dom": "^16.13.1",
diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json
index 7cac2e6a68..9434ec70c8 100644
--- a/plugins/cloudbuild/package.json
+++ b/plugins/cloudbuild/package.json
@@ -37,8 +37,6 @@
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
- "@octokit/rest": "^18.0.0",
- "@octokit/types": "^5.4.1",
"moment": "^2.27.0",
"qs": "^6.9.4",
"react": "^16.13.1",
diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json
index 94b69935d0..f876eb9e7c 100644
--- a/plugins/github-actions/package.json
+++ b/plugins/github-actions/package.json
@@ -40,8 +40,7 @@
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
- "@octokit/rest": "^18.0.0",
- "@octokit/types": "^5.4.1",
+ "@octokit/rest": "^18.0.12",
"moment": "^2.27.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
diff --git a/plugins/github-actions/src/api/GithubActionsApi.ts b/plugins/github-actions/src/api/GithubActionsApi.ts
index bf79e74351..75086998c9 100644
--- a/plugins/github-actions/src/api/GithubActionsApi.ts
+++ b/plugins/github-actions/src/api/GithubActionsApi.ts
@@ -15,12 +15,7 @@
*/
import { createApiRef } from '@backstage/core';
-import {
- ActionsListWorkflowRunsForRepoResponseData,
- ActionsGetWorkflowResponseData,
- ActionsGetWorkflowRunResponseData,
- EndpointInterface,
-} from '@octokit/types';
+import { RestEndpointMethodTypes } from '@octokit/rest';
export const githubActionsApiRef = createApiRef({
id: 'plugin.githubactions.service',
@@ -42,7 +37,9 @@ export type GithubActionsApi = {
pageSize?: number;
page?: number;
branch?: string;
- }) => Promise;
+ }) => Promise<
+ RestEndpointMethodTypes['actions']['listWorkflowRuns']['response']['data']
+ >;
getWorkflow: ({
token,
owner,
@@ -53,7 +50,9 @@ export type GithubActionsApi = {
owner: string;
repo: string;
id: number;
- }) => Promise;
+ }) => Promise<
+ RestEndpointMethodTypes['actions']['getWorkflow']['response']['data']
+ >;
getWorkflowRun: ({
token,
owner,
@@ -64,7 +63,9 @@ export type GithubActionsApi = {
owner: string;
repo: string;
id: number;
- }) => Promise;
+ }) => Promise<
+ RestEndpointMethodTypes['actions']['getWorkflowRun']['response']['data']
+ >;
reRunWorkflow: ({
token,
owner,
@@ -86,5 +87,7 @@ export type GithubActionsApi = {
owner: string;
repo: string;
runId: number;
- }) => Promise;
+ }) => Promise<
+ RestEndpointMethodTypes['actions']['downloadJobLogsForWorkflowRun']['response']['data']
+ >;
};
diff --git a/plugins/github-actions/src/api/GithubActionsClient.ts b/plugins/github-actions/src/api/GithubActionsClient.ts
index 94fb774537..5be2c9a743 100644
--- a/plugins/github-actions/src/api/GithubActionsClient.ts
+++ b/plugins/github-actions/src/api/GithubActionsClient.ts
@@ -15,13 +15,7 @@
*/
import { GithubActionsApi } from './GithubActionsApi';
-import { Octokit } from '@octokit/rest';
-import {
- ActionsListWorkflowRunsForRepoResponseData,
- ActionsGetWorkflowResponseData,
- ActionsGetWorkflowRunResponseData,
- EndpointInterface,
-} from '@octokit/types';
+import { Octokit, RestEndpointMethodTypes } from '@octokit/rest';
export class GithubActionsClient implements GithubActionsApi {
async reRunWorkflow({
@@ -55,7 +49,9 @@ export class GithubActionsClient implements GithubActionsApi {
pageSize?: number;
page?: number;
branch?: string;
- }): Promise {
+ }): Promise<
+ RestEndpointMethodTypes['actions']['listWorkflowRuns']['response']['data']
+ > {
const workflowRuns = await new Octokit({
auth: token,
}).actions.listWorkflowRunsForRepo({
@@ -77,7 +73,9 @@ export class GithubActionsClient implements GithubActionsApi {
owner: string;
repo: string;
id: number;
- }): Promise {
+ }): Promise<
+ RestEndpointMethodTypes['actions']['getWorkflow']['response']['data']
+ > {
const workflow = await new Octokit({ auth: token }).actions.getWorkflow({
owner,
repo,
@@ -95,7 +93,9 @@ export class GithubActionsClient implements GithubActionsApi {
owner: string;
repo: string;
id: number;
- }): Promise {
+ }): Promise<
+ RestEndpointMethodTypes['actions']['getWorkflowRun']['response']['data']
+ > {
const run = await new Octokit({ auth: token }).actions.getWorkflowRun({
owner,
repo,
@@ -113,7 +113,9 @@ export class GithubActionsClient implements GithubActionsApi {
owner: string;
repo: string;
runId: number;
- }): Promise {
+ }): Promise<
+ RestEndpointMethodTypes['actions']['downloadJobLogsForWorkflowRun']['response']['data']
+ > {
const workflow = await new Octokit({
auth: token,
}).actions.downloadJobLogsForWorkflowRun({
diff --git a/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx b/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx
index a2753589eb..ac238b8d0f 100644
--- a/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx
+++ b/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx
@@ -209,8 +209,8 @@ export const WorkflowRunDetails = ({ entity }: { entity: Entity }) => {
@@ -218,7 +218,7 @@ export const WorkflowRunDetails = ({ entity }: { entity: Entity }) => {
Author
- {`${details.value?.head_commit.author.name} (${details.value?.head_commit.author.email})`}
+ {`${details.value?.head_commit.author?.name} (${details.value?.head_commit.author?.email})`}
diff --git a/plugins/github-actions/src/components/useWorkflowRuns.ts b/plugins/github-actions/src/components/useWorkflowRuns.ts
index d0be852beb..4261012c26 100644
--- a/plugins/github-actions/src/components/useWorkflowRuns.ts
+++ b/plugins/github-actions/src/components/useWorkflowRuns.ts
@@ -18,7 +18,6 @@ import { useAsyncRetry } from 'react-use';
import { WorkflowRun } from './WorkflowRunsTable/WorkflowRunsTable';
import { githubActionsApiRef } from '../api/GithubActionsApi';
import { useApi, githubAuthApiRef, errorApiRef } from '@backstage/core';
-import { ActionsListWorkflowRunsForRepoResponseData } from '@octokit/types';
export function useWorkflowRuns({
owner,
@@ -55,44 +54,40 @@ export function useWorkflowRuns({
page: page + 1,
branch,
})
- .then(
- (
- workflowRunsData: ActionsListWorkflowRunsForRepoResponseData,
- ): WorkflowRun[] => {
- setTotal(workflowRunsData.total_count);
- // Transformation here
- return workflowRunsData.workflow_runs.map(run => ({
- message: run.head_commit.message,
- id: `${run.id}`,
- onReRunClick: async () => {
- try {
- await api.reRunWorkflow({
- token,
- owner,
- repo,
- runId: run.id,
- });
- } catch (e) {
- errorApi.post(e);
- }
+ .then((workflowRunsData): WorkflowRun[] => {
+ setTotal(workflowRunsData.total_count);
+ // Transformation here
+ return workflowRunsData.workflow_runs.map(run => ({
+ message: run.head_commit.message,
+ id: `${run.id}`,
+ onReRunClick: async () => {
+ try {
+ await api.reRunWorkflow({
+ token,
+ owner,
+ repo,
+ runId: run.id,
+ });
+ } catch (e) {
+ errorApi.post(e);
+ }
+ },
+ source: {
+ branchName: run.head_branch,
+ commit: {
+ hash: run.head_commit.id,
+ url: run.head_repository.branches_url.replace(
+ '{/branch}',
+ run.head_branch,
+ ),
},
- source: {
- branchName: run.head_branch,
- commit: {
- hash: run.head_commit.id,
- url: run.head_repository.branches_url.replace(
- '{/branch}',
- run.head_branch,
- ),
- },
- },
- status: run.status,
- conclusion: run.conclusion,
- url: run.url,
- githubUrl: run.html_url,
- }));
- },
- )
+ },
+ status: run.status,
+ conclusion: run.conclusion,
+ url: run.url,
+ githubUrl: run.html_url,
+ }));
+ })
);
}, [page, pageSize, repo, owner]);
diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json
index efbc73ff4b..7855dcd349 100644
--- a/plugins/scaffolder-backend/package.json
+++ b/plugins/scaffolder-backend/package.json
@@ -35,7 +35,7 @@
"@backstage/integration": "^0.1.5",
"@gitbeaker/core": "^28.0.2",
"@gitbeaker/node": "^28.0.2",
- "@octokit/rest": "^18.0.0",
+ "@octokit/rest": "^18.0.12",
"@types/dockerode": "^3.2.1",
"@types/express": "^4.17.6",
"azure-devops-node-api": "^10.1.1",
@@ -60,7 +60,6 @@
"devDependencies": {
"@backstage/cli": "^0.4.5",
"@backstage/test-utils": "^0.1.5",
- "@octokit/types": "^5.4.1",
"@types/fs-extra": "^9.0.1",
"@types/git-url-parse": "^9.0.0",
"@types/supertest": "^2.0.8",
diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts
index 22f1cd3172..2487076605 100644
--- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts
+++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts
@@ -17,15 +17,10 @@
jest.mock('@octokit/rest');
jest.mock('./helpers');
-import { Octokit } from '@octokit/rest';
-import {
- OctokitResponse,
- ReposCreateInOrgResponseData,
- UsersGetByUsernameResponseData,
-} from '@octokit/types';
+import { getVoidLogger } from '@backstage/backend-common';
+import { Octokit, RestEndpointMethodTypes } from '@octokit/rest';
import { GithubPublisher } from './github';
import { initRepoAndPush } from './helpers';
-import { getVoidLogger } from '@backstage/backend-common';
const { mockGithubClient } = require('@octokit/rest') as {
mockGithubClient: {
@@ -54,12 +49,12 @@ describe('GitHub Publisher', () => {
data: {
clone_url: 'https://github.com/backstage/backstage.git',
},
- } as OctokitResponse);
+ } as RestEndpointMethodTypes['repos']['createInOrg']['response']);
mockGithubClient.users.getByUsername.mockResolvedValue({
data: {
type: 'Organization',
},
- } as OctokitResponse);
+ } as RestEndpointMethodTypes['users']['getByUsername']['response']);
const result = await publisher.publish({
values: {
@@ -104,12 +99,12 @@ describe('GitHub Publisher', () => {
data: {
clone_url: 'https://github.com/backstage/backstage.git',
},
- } as OctokitResponse);
+ } as RestEndpointMethodTypes['repos']['createForAuthenticatedUser']['response']);
mockGithubClient.users.getByUsername.mockResolvedValue({
data: {
type: 'User',
},
- } as OctokitResponse);
+ } as RestEndpointMethodTypes['users']['getByUsername']['response']);
const result = await publisher.publish({
values: {
@@ -148,12 +143,12 @@ describe('GitHub Publisher', () => {
data: {
clone_url: 'https://github.com/backstage/backstage.git',
},
- } as OctokitResponse);
+ } as RestEndpointMethodTypes['repos']['createForAuthenticatedUser']['response']);
mockGithubClient.users.getByUsername.mockResolvedValue({
data: {
type: 'User',
},
- } as OctokitResponse);
+ } as RestEndpointMethodTypes['users']['getByUsername']['response']);
const result = await publisher.publish({
values: {
@@ -205,12 +200,12 @@ describe('GitHub Publisher', () => {
data: {
clone_url: 'https://github.com/backstage/backstage.git',
},
- } as OctokitResponse);
+ } as RestEndpointMethodTypes['repos']['createInOrg']['response']);
mockGithubClient.users.getByUsername.mockResolvedValue({
data: {
type: 'Organization',
},
- } as OctokitResponse);
+ } as RestEndpointMethodTypes['users']['getByUsername']['response']);
const result = await publisher.publish({
values: {
@@ -254,12 +249,12 @@ describe('GitHub Publisher', () => {
data: {
clone_url: 'https://github.com/backstage/backstage.git',
},
- } as OctokitResponse);
+ } as RestEndpointMethodTypes['repos']['createForAuthenticatedUser']['response']);
mockGithubClient.users.getByUsername.mockResolvedValue({
data: {
type: 'User',
},
- } as OctokitResponse);
+ } as RestEndpointMethodTypes['users']['getByUsername']['response']);
const result = await publisher.publish({
values: {
diff --git a/yarn.lock b/yarn.lock
index 339e810697..b10819d4f2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4668,6 +4668,13 @@
dependencies:
"@octokit/types" "^2.0.0"
+"@octokit/auth-token@^2.4.4":
+ version "2.4.4"
+ resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.4.tgz#ee31c69b01d0378c12fd3ffe406030f3d94d3b56"
+ integrity sha512-LNfGu3Ro9uFAYh10MUZVaT7X2CnNm2C8IDQmabx+3DygYIQjs9FwzFAHN/0t6mu5HEPhxcb1XOuxdpY82vCg2Q==
+ dependencies:
+ "@octokit/types" "^6.0.0"
+
"@octokit/core@^3.0.0":
version "3.1.0"
resolved "https://registry.npmjs.org/@octokit/core/-/core-3.1.0.tgz#9c3c9b23f7504668cfa057f143ccbf0c645a0ac9"
@@ -4680,6 +4687,18 @@
before-after-hook "^2.1.0"
universal-user-agent "^5.0.0"
+"@octokit/core@^3.2.3":
+ version "3.2.4"
+ resolved "https://registry.npmjs.org/@octokit/core/-/core-3.2.4.tgz#5791256057a962eca972e31818f02454897fd106"
+ integrity sha512-d9dTsqdePBqOn7aGkyRFe7pQpCXdibSJ5SFnrTr0axevObZrpz3qkWm7t/NjYv5a66z6vhfteriaq4FRz3e0Qg==
+ dependencies:
+ "@octokit/auth-token" "^2.4.4"
+ "@octokit/graphql" "^4.5.8"
+ "@octokit/request" "^5.4.12"
+ "@octokit/types" "^6.0.3"
+ before-after-hook "^2.1.0"
+ universal-user-agent "^6.0.0"
+
"@octokit/endpoint@^6.0.1":
version "6.0.3"
resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.3.tgz#dd09b599662d7e1b66374a177ab620d8cdf73487"
@@ -4698,19 +4717,19 @@
"@octokit/types" "^5.0.0"
universal-user-agent "^5.0.0"
-"@octokit/graphql@^4.5.6":
- version "4.5.6"
- resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.6.tgz#708143ba15cf7c1879ed6188266e7f270be805d4"
- integrity sha512-Rry+unqKTa3svswT2ZAuqenpLrzJd+JTv89LTeVa5UM/5OX8o4KTkPL7/1ABq4f/ZkELb0XEK/2IEoYwykcLXg==
+"@octokit/graphql@^4.5.8":
+ version "4.5.8"
+ resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.8.tgz#d42373633c3015d0eafce64a8ce196be167fdd9b"
+ integrity sha512-WnCtNXWOrupfPJgXe+vSmprZJUr0VIu14G58PMlkWGj3cH+KLZEfKMmbUQ6C3Wwx6fdhzVW1CD5RTnBdUHxhhA==
dependencies:
"@octokit/request" "^5.3.0"
- "@octokit/types" "^5.0.0"
+ "@octokit/types" "^6.0.0"
universal-user-agent "^6.0.0"
-"@octokit/openapi-types@^2.0.0":
- version "2.0.1"
- resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-2.0.1.tgz#7453d8281ce66b8ed1607f7ac7d751c3baffd2cc"
- integrity sha512-9AuC04PUnZrjoLiw3uPtwGh9FE4Q3rTqs51oNlQ0rkwgE8ftYsOC+lsrQyvCvWm85smBbSc0FNRKKumvGyb44Q==
+"@octokit/openapi-types@^2.2.0":
+ version "2.2.0"
+ resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-2.2.0.tgz#123e0438a0bc718ccdac3b5a2e69b3dd00daa85b"
+ integrity sha512-274lNUDonw10kT8wHg8fCcUc1ZjZHbWv0/TbAwb0ojhBQqZYc1cQ/4yqTVTtPMDeZ//g7xVEYe/s3vURkRghPg==
"@octokit/plugin-enterprise-rest@^6.0.1":
version "6.0.1"
@@ -4731,11 +4750,23 @@
dependencies:
"@octokit/types" "^5.0.0"
+"@octokit/plugin-paginate-rest@^2.6.2":
+ version "2.7.0"
+ resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.7.0.tgz#6bb7b043c246e0654119a6ec4e72a172c9e2c7f3"
+ integrity sha512-+zARyncLjt9b0FjqPAbJo4ss7HOlBi1nprq+cPlw5vu2+qjy7WvlXhtXFdRHQbSL1Pt+bfAKaLADEkkvg8sP8w==
+ dependencies:
+ "@octokit/types" "^6.0.1"
+
"@octokit/plugin-request-log@^1.0.0":
version "1.0.0"
resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz#eef87a431300f6148c39a7f75f8cfeb218b2547e"
integrity sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw==
+"@octokit/plugin-request-log@^1.0.2":
+ version "1.0.2"
+ resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.2.tgz#394d59ec734cd2f122431fbaf05099861ece3c44"
+ integrity sha512-oTJSNAmBqyDR41uSMunLQKMX0jmEXbwD1fpz8FG27lScV3RhtGfBa1/BBLym+PxcC16IBlF7KH9vP1BUYxA+Eg==
+
"@octokit/plugin-rest-endpoint-methods@2.4.0":
version "2.4.0"
resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz#3288ecf5481f68c494dd0602fc15407a59faf61e"
@@ -4752,12 +4783,12 @@
"@octokit/types" "^5.4.1"
deprecation "^2.3.1"
-"@octokit/plugin-rest-endpoint-methods@4.2.0":
- version "4.2.0"
- resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.2.0.tgz#c5a0691b3aba5d8b4ef5dffd6af3649608f167ba"
- integrity sha512-1/qn1q1C1hGz6W/iEDm9DoyNoG/xdFDt78E3eZ5hHeUfJTLJgyAMdj9chL/cNBHjcjd+FH5aO1x0VCqR2RE0mw==
+"@octokit/plugin-rest-endpoint-methods@4.4.1":
+ version "4.4.1"
+ resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.4.1.tgz#105cf93255432155de078c9efc33bd4e14d1cd63"
+ integrity sha512-+v5PcvrUcDeFXf8hv1gnNvNLdm4C0+2EiuWt9EatjjUmfriM1pTMM+r4j1lLHxeBQ9bVDmbywb11e3KjuavieA==
dependencies:
- "@octokit/types" "^5.5.0"
+ "@octokit/types" "^6.1.0"
deprecation "^2.3.1"
"@octokit/request-error@^1.0.2":
@@ -4792,6 +4823,20 @@
once "^1.4.0"
universal-user-agent "^5.0.0"
+"@octokit/request@^5.4.12":
+ version "5.4.12"
+ resolved "https://registry.npmjs.org/@octokit/request/-/request-5.4.12.tgz#b04826fa934670c56b135a81447be2c1723a2ffc"
+ integrity sha512-MvWYdxengUWTGFpfpefBBpVmmEYfkwMoxonIB3sUGp5rhdgwjXL1ejo6JbgzG/QD9B/NYt/9cJX1pxXeSIUCkg==
+ dependencies:
+ "@octokit/endpoint" "^6.0.1"
+ "@octokit/request-error" "^2.0.0"
+ "@octokit/types" "^6.0.3"
+ deprecation "^2.0.0"
+ is-plain-object "^5.0.0"
+ node-fetch "^2.6.1"
+ once "^1.4.0"
+ universal-user-agent "^6.0.0"
+
"@octokit/rest@^16.28.4":
version "16.43.1"
resolved "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.1.tgz#3b11e7d1b1ac2bbeeb23b08a17df0b20947eda6b"
@@ -4824,15 +4869,15 @@
"@octokit/plugin-request-log" "^1.0.0"
"@octokit/plugin-rest-endpoint-methods" "4.1.4"
-"@octokit/rest@^18.0.6":
- version "18.0.6"
- resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.6.tgz#76c274f1a68f40741a131768ef483f041e7b98b6"
- integrity sha512-ES4lZBKPJMX/yUoQjAZiyFjei9pJ4lTTfb9k7OtYoUzKPDLl/M8jiHqt6qeSauyU4eZGLw0sgP1WiQl9FYeM5w==
+"@octokit/rest@^18.0.12":
+ version "18.0.12"
+ resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.12.tgz#278bd41358c56d87c201e787e8adc0cac132503a"
+ integrity sha512-hNRCZfKPpeaIjOVuNJzkEL6zacfZlBPV8vw8ReNeyUkVvbuCvvrrx8K8Gw2eyHHsmd4dPlAxIXIZ9oHhJfkJpw==
dependencies:
- "@octokit/core" "^3.0.0"
- "@octokit/plugin-paginate-rest" "^2.2.0"
- "@octokit/plugin-request-log" "^1.0.0"
- "@octokit/plugin-rest-endpoint-methods" "4.2.0"
+ "@octokit/core" "^3.2.3"
+ "@octokit/plugin-paginate-rest" "^2.6.2"
+ "@octokit/plugin-request-log" "^1.0.2"
+ "@octokit/plugin-rest-endpoint-methods" "4.4.1"
"@octokit/types@^2.0.0", "@octokit/types@^2.0.1":
version "2.5.0"
@@ -4855,11 +4900,12 @@
dependencies:
"@types/node" ">= 8"
-"@octokit/types@^5.5.0":
- version "5.5.0"
- resolved "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz#e5f06e8db21246ca102aa28444cdb13ae17a139b"
- integrity sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ==
+"@octokit/types@^6.0.0", "@octokit/types@^6.0.1", "@octokit/types@^6.0.3", "@octokit/types@^6.1.0":
+ version "6.2.1"
+ resolved "https://registry.npmjs.org/@octokit/types/-/types-6.2.1.tgz#7f881fe44475ab1825776a4a59ca1ae082ed1043"
+ integrity sha512-jHs9OECOiZxuEzxMZcXmqrEO8GYraHF+UzNVH2ACYh8e/Y7YoT+hUf9ldvVd6zIvWv4p3NdxbQ0xx3ku5BnSiA==
dependencies:
+ "@octokit/openapi-types" "^2.2.0"
"@types/node" ">= 8"
"@open-draft/until@^1.0.3":
@@ -15739,6 +15785,11 @@ is-plain-object@^3.0.0:
dependencies:
isobject "^4.0.0"
+is-plain-object@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
+ integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
+
is-potential-custom-element-name@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397"
From 5a1368ba12aef8c01f06dafc67ef62a5495ef4ba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?=
Date: Tue, 12 Jan 2021 20:21:31 +0100
Subject: [PATCH 37/92] graphiql: update readme
---
.changeset/many-dodos-scream.md | 5 ++
plugins/graphiql/README.md | 89 ++++++++++++++++++---------------
plugins/graphiql/src/plugin.ts | 3 +-
3 files changed, 57 insertions(+), 40 deletions(-)
create mode 100644 .changeset/many-dodos-scream.md
diff --git a/.changeset/many-dodos-scream.md b/.changeset/many-dodos-scream.md
new file mode 100644
index 0000000000..2cae9a3713
--- /dev/null
+++ b/.changeset/many-dodos-scream.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-graphiql': patch
+---
+
+Updated README
diff --git a/plugins/graphiql/README.md b/plugins/graphiql/README.md
index d2f8cafbfb..c75d8b71e3 100644
--- a/plugins/graphiql/README.md
+++ b/plugins/graphiql/README.md
@@ -12,53 +12,64 @@ By exposing GraphiQL as a plugin instead of a standalone app, it's possible to p
Start out by installing the plugin in your Backstage app:
```bash
+cd packages/app
yarn add @backstage/plugin-graphiql
```
-Then add an entry to your App's `plugins.ts` to import the plugin.
+```diff
+# in packages/app/src/plugins.ts
++export { plugin as GraphiQL } from '@backstage/plugin-graphiql';
+```
-The plugin registers a `/graphiql` route, which you can link to from the Sidebar if desired.
+```diff
+# in packages/app/src/App.tsx
++import { Router as GraphiQLRouter } from '@backstage/plugin-graphiql';
+
+ const AppRoutes = () => (
+
++ } />
+```
### Adding GraphQL endpoints
-For the plugin to function, you need to supply GraphQL endpoints through the GraphQLBrowse API, which is done by implementing the `GraphQLBrowseApi` exported by this plugin.
+For the plugin to function, you need to supply GraphQL endpoints through the GraphQLBrowse API. This is done by implementing the `graphQlBrowseApiRef` exported by this plugin.
-If all you need is a static list of endpoints, the plugin exports a `GraphQLEndpoints` class that implements the `GraphQLBrowseApi` for you. Here's and example of how you could expose two GraphQL endpoints in your App:
+If all you need is a static list of endpoints, the plugin exports a `GraphQLEndpoints` class that implements the `GraphQLBrowseApi` for you. Here's an example of how you could expose two GraphQL endpoints in your App:
-```tsx
-import {
- graphQlBrowseApiRef,
- GraphQLEndpoints,
-} from '@backstage/plugin-graphiql';
+```diff
+# in packages/app/src/apis.ts
++import {
++ graphQlBrowseApiRef,
++ GraphQLEndpoints,
++} from '@backstage/plugin-graphiql';
-// Implement the Graph QL browse API using a static list of endpoints
-const graphQlBrowseApi = GraphQLEndpoints.from([
- // Use the .create function if all you need is a static URL and headers.
- GraphQLEndpoints.create({
- id: 'gitlab',
- title: 'GitLab',
- url: 'https://gitlab.com/api/graphql',
- // Optional extra headers
- headers: { Extra: 'Header' },
- }),
- {
- id: 'hooli-search',
- title: 'Hooli Search',
- // Custom fetch function, this one is equivalent to using GraphQLEndpoints.create()
- // with url set to https://internal.hooli.com/search
- fetcher: async (params: any) => {
- return fetch('https://internal.hooli.com/search', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(params),
- }).then(res => res.json());
- },
- },
-]);
-
-// ApiRegistry builder created somewhere in your App
-const builder = ApiRegistry.builder();
-
-// Add the instance to the API registry
-builder.add(graphQlBrowseApiRef, graphQlBrowseApi);
+ export const apis = [
++ createApiFactory({
++ api: graphQlBrowseApiRef,
++ deps: { errorApi: errorApiRef, githubAuthApi: githubAuthApiRef },
++ factory: ({ errorApi, githubAuthApi }) =>
++ GraphQLEndpoints.from([
++ // Use the .create function if all you need is a static URL and headers.
++ GraphQLEndpoints.create({
++ id: 'gitlab',
++ title: 'GitLab',
++ url: 'https://gitlab.com/api/graphql',
++ // Optional extra headers
++ headers: { Extra: 'Header' },
++ }),
++ {
++ id: 'hooli-search',
++ title: 'Hooli Search',
++ // Custom fetch function, this one is equivalent to using GraphQLEndpoints.create()
++ // with url set to https://internal.hooli.com/search
++ fetcher: async (params: any) => {
++ return fetch('https://internal.hooli.com/search', {
++ method: 'POST',
++ headers: { 'Content-Type': 'application/json' },
++ body: JSON.stringify(params),
++ }).then(res => res.json());
++ },
++ }
++ ]),
++ }),
```
diff --git a/plugins/graphiql/src/plugin.ts b/plugins/graphiql/src/plugin.ts
index a0bb79ed29..87f750b132 100644
--- a/plugins/graphiql/src/plugin.ts
+++ b/plugins/graphiql/src/plugin.ts
@@ -25,7 +25,8 @@ import { graphiQLRouteRef } from './route-refs';
export const plugin = createPlugin({
id: 'graphiql',
apis: [
- // GitLab is used as an example endpoint, but most plug
+ // GitLab is used as an example endpoint, but most will want to plug in
+ // their own instead.
createApiFactory(
graphQlBrowseApiRef,
GraphQLEndpoints.from([
From 71fb4e1281b57754ed8cb9765bba2018678d98a4 Mon Sep 17 00:00:00 2001
From: Johan Haals
Date: Wed, 13 Jan 2021 16:58:11 +0100
Subject: [PATCH 38/92] cli: Remove api url from github app configuration
---
.../src/commands/create-github-app/GithubCreateAppServer.ts | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/packages/cli/src/commands/create-github-app/GithubCreateAppServer.ts b/packages/cli/src/commands/create-github-app/GithubCreateAppServer.ts
index 406e563ebc..45671c2ead 100644
--- a/packages/cli/src/commands/create-github-app/GithubCreateAppServer.ts
+++ b/packages/cli/src/commands/create-github-app/GithubCreateAppServer.ts
@@ -47,7 +47,6 @@ const FORM_PAGE = `
type GithubAppConfig = {
appId: number;
- apiUrl: string;
slug?: string;
name?: string;
webhookUrl?: string;
@@ -88,14 +87,11 @@ export class GithubCreateAppServer {
`POST /app-manifests/${encodeURIComponent(
req.query.code as string,
)}/conversions`,
- ).then(({ data, url }) => {
- // url = https://api.github.com/app-manifests//conversions
- const apiUrl = url.replace(/(?:\/[^\/]+){3}$/, '');
+ ).then(({ data }) => {
resolve({
name: data.name,
slug: data.slug,
appId: data.id,
- apiUrl,
webhookUrl: this.webhookUrl,
clientId: data.client_id,
clientSecret: data.client_secret,
From 8277fe6f77094d3254c268ec8e5c64c28221242c Mon Sep 17 00:00:00 2001
From: Johan Haals
Date: Wed, 13 Jan 2021 17:04:10 +0100
Subject: [PATCH 39/92] Add changeset
---
.changeset/real-vans-provide.md | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 .changeset/real-vans-provide.md
diff --git a/.changeset/real-vans-provide.md b/.changeset/real-vans-provide.md
new file mode 100644
index 0000000000..9d83804bd6
--- /dev/null
+++ b/.changeset/real-vans-provide.md
@@ -0,0 +1,5 @@
+---
+'@backstage/cli': patch
+---
+
+Remove `apiUrl` from the output of the create-github-app because apiUrl already exist in the GitHub integration config.
From 4f78ee3a69571d0fed625a5120f64ffb940ddbdf Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 13 Jan 2021 16:35:56 +0000
Subject: [PATCH 40/92] chore(deps): bump azure-devops-node-api from 10.1.1 to
10.2.1
Bumps [azure-devops-node-api](https://github.com/Microsoft/azure-devops-node-api) from 10.1.1 to 10.2.1.
- [Release notes](https://github.com/Microsoft/azure-devops-node-api/releases)
- [Commits](https://github.com/Microsoft/azure-devops-node-api/commits)
Signed-off-by: dependabot[bot]
---
yarn.lock | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/yarn.lock b/yarn.lock
index b10819d4f2..1981e1f886 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8560,12 +8560,12 @@ axobject-query@^2.0.2:
integrity sha512-ICt34ZmrVt8UQnvPl6TVyDTkmhXmAyAT4Jh5ugfGUX4MOrZ+U/ZY6/sdylRw3qGNr9Ub5AJsaHeDMzNLehRdOQ==
azure-devops-node-api@^10.1.1:
- version "10.1.1"
- resolved "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-10.1.1.tgz#9016d8935316fff260f5f8fafd81d0caff90a19e"
- integrity sha512-P4Hyrh/+Nzc2KXQk73z72/GsenSWIH5o8uiyELqykJYs9TWTVCxVwghoR7lPeiY6QVoXkq2S2KtvAgi5fyjl9w==
+ version "10.2.1"
+ resolved "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-10.2.1.tgz#835080164f8c30cec6506c47198b044c053f1f36"
+ integrity sha512-XuSiUaYpk0tQpd9fD8qfRa5y1IdavupKNVmwxy0w/RhmxG2Wl8uAYnNJchUoWd3Rn9On0mYTCCZSn+UlYdYFSg==
dependencies:
tunnel "0.0.6"
- typed-rest-client "^1.7.3"
+ typed-rest-client "^1.8.0"
underscore "1.8.3"
babel-code-frame@^6.22.0:
@@ -24977,10 +24977,10 @@ type@^2.0.0:
resolved "https://registry.npmjs.org/type/-/type-2.0.0.tgz#5f16ff6ef2eb44f260494dae271033b29c09a9c3"
integrity sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==
-typed-rest-client@^1.7.3:
- version "1.7.3"
- resolved "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.7.3.tgz#1beb263b86b14d34596f6127c6172dd5fd652e7b"
- integrity sha512-CwTpx/TkRHGZoHkJhBcp4X8K3/WtlzSHVQR0OIFnt10j4tgy4ypgq/SrrgVpA1s6tAL49Q6J3R5C0Cgfh2ddqA==
+typed-rest-client@^1.8.0:
+ version "1.8.0"
+ resolved "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.0.tgz#3b6c22a7cc31b665ec1e4bedb3482ebe12e2fbe6"
+ integrity sha512-Nu1MrdH6ECrRW5gHoRAdubgCs4oH6q5/J76jsEC8bVDfvVoVPkigukPalhMHPwb7ZvpsZqPptd5zpt/QdtrdBw==
dependencies:
qs "^6.9.1"
tunnel "0.0.6"
From cb7af51e7367e57af5c555b49ceda8a92a490c72 Mon Sep 17 00:00:00 2001
From: Himanshu Mishra
Date: Wed, 13 Jan 2021 21:08:03 +0100
Subject: [PATCH 41/92] techdocs: cache docs site when built using urlReader
for 30 minutes
This caching makes it usable experience, so that docs are not built on every load.
In future readTree will support a method to fetch the timestamp of the latest HEAD. And
it should be used to invalidate the cache.
---
.changeset/techdocs-rotten-crabs-ring.md | 5 +++++
plugins/techdocs-backend/src/DocsBuilder/builder.ts | 12 ++++++++++++
2 files changed, 17 insertions(+)
create mode 100644 .changeset/techdocs-rotten-crabs-ring.md
diff --git a/.changeset/techdocs-rotten-crabs-ring.md b/.changeset/techdocs-rotten-crabs-ring.md
new file mode 100644
index 0000000000..7eddd8ba6c
--- /dev/null
+++ b/.changeset/techdocs-rotten-crabs-ring.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-techdocs-backend': patch
+---
+
+If using Url Reader, cache downloaded source files for 30 minutes.
diff --git a/plugins/techdocs-backend/src/DocsBuilder/builder.ts b/plugins/techdocs-backend/src/DocsBuilder/builder.ts
index 3d40f0a870..33d3120c47 100644
--- a/plugins/techdocs-backend/src/DocsBuilder/builder.ts
+++ b/plugins/techdocs-backend/src/DocsBuilder/builder.ts
@@ -144,6 +144,18 @@ export class DocsBuilder {
}
}
+ // Cache downloaded source files for 30 minutes.
+ // TODO: When urlReader/readTree supports some way to get latest commit timestamp,
+ // it should be used to invalidate cache.
+ if (type === 'url') {
+ const builtAt = buildMetadataStorage.getTimestamp();
+ const now = Date.now();
+
+ if (builtAt > now - 1800000) {
+ return true;
+ }
+ }
+
this.logger.debug(
`Docs for entity ${getEntityId(this.entity)} was outdated.`,
);
From dc33c518904549716a8af0cc5cc73fbe2f15388d Mon Sep 17 00:00:00 2001
From: Himanshu Mishra
Date: Wed, 13 Jan 2021 21:35:36 +0100
Subject: [PATCH 42/92] TechDocs: Use URL Reader in the out-of-the-box
experience
It is time to start using URL Reader for the exmaple docs components we provide in the out-of-the-box experience (i.e. when users experience TechDocs by doing a git clone of this repository).
URL Reader makes the prepare step 8x faster.
---
app-config.yaml | 4 ++--
catalog-info.yaml | 2 +-
.../{documented-component.yaml => catalog-info.yaml} | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
rename plugins/techdocs-backend/examples/documented-component/{documented-component.yaml => catalog-info.yaml} (61%)
diff --git a/app-config.yaml b/app-config.yaml
index d2100801ae..ac9cf700df 100644
--- a/app-config.yaml
+++ b/app-config.yaml
@@ -178,9 +178,9 @@ catalog:
# Example component for github-actions
- type: url
target: https://github.com/backstage/backstage/blob/master/plugins/github-actions/examples/sample.yaml
- # Example component for techdocs
+ # Example component for TechDocs
- type: url
- target: https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component/documented-component.yaml
+ target: https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component/catalog-info.yaml
# Backstage example APIs
- type: url
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-apis.yaml
diff --git a/catalog-info.yaml b/catalog-info.yaml
index 617d01093e..2144d1709c 100644
--- a/catalog-info.yaml
+++ b/catalog-info.yaml
@@ -6,7 +6,7 @@ metadata:
Backstage is an open-source developer portal that puts the developer experience first.
annotations:
github.com/project-slug: backstage/backstage
- backstage.io/techdocs-ref: github:https://github.com/backstage/backstage.git
+ backstage.io/techdocs-ref: url:https://github.com/backstage/backstage/tree/master
lighthouse.com/website-url: https://backstage.io
spec:
type: library
diff --git a/plugins/techdocs-backend/examples/documented-component/documented-component.yaml b/plugins/techdocs-backend/examples/documented-component/catalog-info.yaml
similarity index 61%
rename from plugins/techdocs-backend/examples/documented-component/documented-component.yaml
rename to plugins/techdocs-backend/examples/documented-component/catalog-info.yaml
index 800116344f..d609866f9b 100644
--- a/plugins/techdocs-backend/examples/documented-component/documented-component.yaml
+++ b/plugins/techdocs-backend/examples/documented-component/catalog-info.yaml
@@ -4,7 +4,7 @@ metadata:
name: documented-component
description: A Service with TechDocs documentation
annotations:
- backstage.io/techdocs-ref: 'github:https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component'
+ backstage.io/techdocs-ref: 'url:https://github.com/backstage/backstage/tree/master/plugins/techdocs-backend/examples/documented-component'
spec:
type: service
lifecycle: experimental
From 99a778ac9fe78facde50c5aaf497de1c4b84a8f4 Mon Sep 17 00:00:00 2001
From: Kevin Lee
Date: Tue, 12 Jan 2021 12:45:10 -0800
Subject: [PATCH 43/92] Use history.pushState for hash link navigation in
techdocs
---
.changeset/small-berries-travel.md | 5 +++++
plugins/techdocs/src/reader/components/Reader.tsx | 10 +++++++++-
2 files changed, 14 insertions(+), 1 deletion(-)
create mode 100644 .changeset/small-berries-travel.md
diff --git a/.changeset/small-berries-travel.md b/.changeset/small-berries-travel.md
new file mode 100644
index 0000000000..af77ee2a17
--- /dev/null
+++ b/.changeset/small-berries-travel.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-techdocs': patch
+---
+
+Use `history.pushState` for hash link navigation.
diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx
index fb49b90096..1264e2b6eb 100644
--- a/plugins/techdocs/src/reader/components/Reader.tsx
+++ b/plugins/techdocs/src/reader/components/Reader.tsx
@@ -115,7 +115,15 @@ export const Reader = ({ entityId, onReady }: Props) => {
baseUrl: window.location.origin,
onClick: (_: MouseEvent, url: string) => {
const parsedUrl = new URL(url);
- navigate(`${parsedUrl.pathname}${parsedUrl.hash}`);
+ if (parsedUrl.hash) {
+ history.pushState(
+ null,
+ '',
+ `${parsedUrl.pathname}${parsedUrl.hash}`,
+ );
+ } else {
+ navigate(parsedUrl.pathname);
+ }
shadowRoot?.querySelector(parsedUrl.hash)?.scrollIntoView();
},
From e5d12f705b34e00611ee2b0f46c132f7f265c5e5 Mon Sep 17 00:00:00 2001
From: Himanshu Mishra
Date: Wed, 13 Jan 2021 23:22:16 +0100
Subject: [PATCH 44/92] techdocs: update changeset to unblock from maintainers
review
---
.../{small-berries-travel.md => techdocs-small-berries-travel.md} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename .changeset/{small-berries-travel.md => techdocs-small-berries-travel.md} (100%)
diff --git a/.changeset/small-berries-travel.md b/.changeset/techdocs-small-berries-travel.md
similarity index 100%
rename from .changeset/small-berries-travel.md
rename to .changeset/techdocs-small-berries-travel.md
From 3e9b38e288231d070777cd651cd3727b5705b4b6 Mon Sep 17 00:00:00 2001
From: Patrik Oldsberg
Date: Sun, 10 Jan 2021 18:11:23 +0100
Subject: [PATCH 45/92] docs: initial incomplete composability docs
---
docs/plugins/composability.md | 86 +++++++++++++++++++++++++++++++++++
1 file changed, 86 insertions(+)
create mode 100644 docs/plugins/composability.md
diff --git a/docs/plugins/composability.md b/docs/plugins/composability.md
new file mode 100644
index 0000000000..d85a557772
--- /dev/null
+++ b/docs/plugins/composability.md
@@ -0,0 +1,86 @@
+---
+id: composability
+title: New Composability System
+description:
+ Documentation and migration instructions for new composability APIs.
+---
+
+## Summary
+
+This page describes the new composability system that was recently introduced in
+Backstage. It describes the new system from the perspective of the existing
+patterns and APIs. As the new system is solidified and existing code is ported,
+this page will removed and replaced with a more direct description of the
+composability system.
+
+The core principle of the new composability system is that plugins should have
+clear boundaries and connections. It should isolate crashes within a plugin, but
+allow navigation between them. It should allow for plugins to be loaded only
+when needed, and enable plugins to provide extension point for other plugins to
+build upon. The composability system is also built with an app-first mindset,
+prioritizing simplicity and clarity in the app over plugins and core APIs.
+
+The new composability system isn't a single new API surface. It is a collection
+of patterns, new primitives, new APIs, and old APIs used in new ways. At the
+core is the new concept of Extensions, are exported by plugins for use in the
+app. There is a new primitive called component data, which is used to connect
+plugin and the app, and a new hook that provides a practical use of .
+
+## Component Data
+
+Component data is a new composability primitive that is introduced as a way to
+provide a new data dimension for React components. Data is attached to React
+components using a key, and is then readable from any JSX elements created with
+those components using the same key, as illustrated by the following example:
+
+```tsx
+const MyComponent = () =>
This is my component
;
+attachComponentData(MyComponent, 'my.data', 5);
+
+const element = ;
+const myData = getComponentData(element, 'my.data');
+// myData === 5
+```
+
+The purpose of component data is to provide a method for embedding data that can
+be inspected before rendering elements. It's a pattern that is quite common
+among React libraries, and used for example by `react-router` and `material-ui`
+to discover properties of the child elements before rendering. Although in those
+libraries only the element type and props are typically inspected, while our
+component data adds more structured access and simplifies evolution by allowing
+for multiple different versions of a piece of data to be used at once.
+
+The main use-case
+
+## Extensions
+
+Extensions are what plugins export for use in an app. Most typically they are
+React components, but in practice they can be any kind of value. They are
+created using `create*Extension` functions, and wrapped with `plugin.provide()`
+in order to create the actual exported extension.
+
+The Backstage core API currently provides two different types of extension
+creators, `createComponentExtension`, and `createRoutableExtension`.
+
+### Extensions from a plugin's point of view
+
+Extensions are one of the primary methods to traverse the plugin boundary, and
+the way that plugins provide concrete content for use within an app. They
+replace existing component export concepts such as `Router` or `*Card`s for
+display on entity overview pages.
+
+### Using Extensions in an app
+
+TODO
+
+## RouteRefs, useRouteRef, and plugin routes and externalRoutes
+
+TODO
+
+## Binding external routes in the app
+
+TODO
+
+## New catalog components, EntitySwitch & EntityLayout, and how to use those in the app
+
+TODO
From 492258d2a10d83fba72d38e5101dc81b0f8795c7 Mon Sep 17 00:00:00 2001
From: Patrik Oldsberg
Date: Tue, 12 Jan 2021 20:23:27 +0100
Subject: [PATCH 46/92] docs: lotsa more composability docs
---
.github/styles/vocab.txt | 2 +
docs/plugins/composability.md | 255 +++++++++++++++++++++++++++++++---
2 files changed, 234 insertions(+), 23 deletions(-)
diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt
index a71887f796..6c38cf638a 100644
--- a/.github/styles/vocab.txt
+++ b/.github/styles/vocab.txt
@@ -183,6 +183,8 @@ rollbar
Rollbar
Rollup
Rosaceae
+routable
+Routable
rst
rsync
rugvip
diff --git a/docs/plugins/composability.md b/docs/plugins/composability.md
index d85a557772..5c96b88cee 100644
--- a/docs/plugins/composability.md
+++ b/docs/plugins/composability.md
@@ -26,7 +26,12 @@ core is the new concept of Extensions, are exported by plugins for use in the
app. There is a new primitive called component data, which is used to connect
plugin and the app, and a new hook that provides a practical use of .
-## Component Data
+## New Concepts
+
+This section is a brief look into all the new and updated concepts that were put
+in place to support the new composability system.
+
+### Component Data
Component data is a new composability primitive that is introduced as a way to
provide a new data dimension for React components. Data is attached to React
@@ -43,44 +48,248 @@ const myData = getComponentData(element, 'my.data');
```
The purpose of component data is to provide a method for embedding data that can
-be inspected before rendering elements. It's a pattern that is quite common
-among React libraries, and used for example by `react-router` and `material-ui`
-to discover properties of the child elements before rendering. Although in those
-libraries only the element type and props are typically inspected, while our
-component data adds more structured access and simplifies evolution by allowing
-for multiple different versions of a piece of data to be used at once.
+be inspected before rendering elements. Element inspection is a pattern that is
+quite common among React libraries, and used for example by `react-router` and
+`material-ui` to discover properties of the child elements before rendering.
+Though in those libraries only the element type and props are typically
+inspected, while our component data adds more structured access and simplifies
+evolution by allowing for multiple different versions of a piece of data to be
+used at once.
-The main use-case
+The initial use-cases for component data is support route and plugin discovery
+through elements in the app. Through this we allow for the React element tree in
+the app to be the source of truth, both for which plugins are used and all
+top-level plugin routes in the app. The use of component data is not limited to
+these use-cases though, as it can be used as a primitive to create new
+abstractions as well.
-## Extensions
+### Extensions
Extensions are what plugins export for use in an app. Most typically they are
React components, but in practice they can be any kind of value. They are
created using `create*Extension` functions, and wrapped with `plugin.provide()`
in order to create the actual exported extension.
-The Backstage core API currently provides two different types of extension
-creators, `createComponentExtension`, and `createRoutableExtension`.
+The extension type is dead simple:
-### Extensions from a plugin's point of view
+```ts
+export type Extension = {
+ expose(plugin: BackstagePlugin): T;
+};
+```
+
+The power of extensions comes from the ability of various actors to hook into
+their usage. The creation and plugin wrapping is controlled by whoever owns the
+creation function, the Backstage core is able to hook into the process of
+exposing the extension outside the plugin, and in the end the app controls the
+usage of the extension.
+
+The Backstage core API currently provides two different types of extension
+creators, `createComponentExtension`, and `createRoutableExtension`. Component
+extensions are plain react component with no particular requirements, such as
+cards for entity overview pages. The component will be exported more or less as
+is, but is wrapped up to provide things like an error boundary, lazy loading,
+and a plugin context.
+
+Routable extensions build on top of component extensions and are used for any
+component that should be rendered at a specific route path, such as full pages
+or entity page tab content. When creating a routable extension you need to
+supply a `RouteRef` as `mountPoint`. The mount point will be the handle of the
+component for the outside world, and is used by other components and plugins
+that wish to link to the routable component.
+
+As of now there are only two extension creation functions, but it is possible to
+add more of them in the future, both in the core library and in plugins that
+wish to provide an extension point for other plugins to build upon. Extensions
+are also not tied to React, and can both be used to model generic JavaScript
+concepts, as well as potentially bridge to rendering libraries and web
+frameworks other than React.
+
+### Extensions from a Plugin's Point of View
Extensions are one of the primary methods to traverse the plugin boundary, and
the way that plugins provide concrete content for use within an app. They
replace existing component export concepts such as `Router` or `*Card`s for
display on entity overview pages.
-### Using Extensions in an app
+It is recommended to create the exported extensions either in the top-level
+`plugin.ts` file, or in a dedicated `extensions.ts` (or `.tsx`) file. That file
+should not contain the bulk of the implementation though, and in fact, if the
+extension is a React component it is recommended to lazy-load the actual
+component. Component extensions support lazy loading out of the box using the
+`lazy` component declaration, for example:
+
+```ts
+export const EntityFooCard = plugin.provide(
+ createComponentExtension({
+ component: {
+ lazy: () => import('./components/FooCard').then(m => m.FooCard),
+ },
+ }),
+);
+```
+
+Routable extensions even enforce lazy loading, for example:
+
+```ts
+export const FooPage = plugin.provide(
+ createRoutableExtension({
+ component: () => import('./components/FooPage').then(m => m.FooPage),
+ mountPoint: fooRouteRef,
+ }),
+);
+```
+
+### Using Extensions in an App
+
+Right now all extensions are modelled as React components. The usage of these
+extension is like regular usage of any React components, with one important
+difference. Extensions must be all be part of a single React element tree
+spanning from the root `AppProvider`.
+
+For example, the following app code does **NOT** work:
+
+```tsx
+const AppRoutes = () => (
+
+ } />
+ } />
+
+);
+
+const App = () => (
+
+
+
+
+
+
+
+);
+```
+
+But it is simple to fix! Simply make sure that you don't create any intermediate
+components in the app, for example like this:
+
+```tsx
+const appRoutes = (
+
+ } />
+ } />
+
+);
+
+const App = () => (
+
+
+ {appRoutes}
+
+
+);
+```
+
+### New Routing System
+
+A big piece of what is enabled by moving over to this new composability system
+is to make `RouteRef`s useful. The `RouteRef`s no longer have their own path, in
+fact the only required parameter is currently a `title`. Instead of assigning a
+path to each `RouteRef` and possibly overriding these paths in the app, the
+concrete `path` for each `RouteRef` is discovered based on the element tree in
+the app. Let's consider the following example:
+
+```tsx
+
+ } />
+ } />
+
+```
+
+We'll assume that `FooPage` and `BarPage` are routable extensions, exported by
+the `fooPlugin` and `barPlugin` respectively. Since the `FooPage` is a routable
+extension it has a `RouteRef` assigned as its mount point, which we'll refer to
+as `fooRootRouteRef`.
+
+Given the above example, the `fooRootRouteRef` will be associated with the
+`'/foo'` route. The path is no longer accessible via the `path` property of the
+`RouteRef` though, as the routing structure is tied to the app's react tree. We
+instead use the new `useRouteRef` hook if we want to create a concrete link to
+the page. The `useRouteRef` hook takes a single `RouteRef` as its only
+parameter, and returns a function that is called to create the URL.
+
+Now let's assume that we want to link from the `BarPage` to the `FooPage`.
+Before the introduction of the new composability system, we would do this by
+importing the `fooRootRouteRef` from the `fooPlugin`. This created an
+unnecessary dependency on the plugin, and also provided little flexibility
+allowing the app to tie plugins together rather than the plugins themselves. To
+handle this, we introduce the concept of `ExternalRouteRef`s. Much like regular
+route refs, they can be passed to `useRouteRef` to create concrete URLs, but
+they can not be used as mount points in routable component and instead have to
+be associated with an actual using route bindings in the app.
+
+The `ExternalRouteRef` inside the `barPlugin` should also not be opinionated
+about what it is linking to either, allowing the app to decide the final target.
+It should however provide context in how the link is presented or used, to make
+it easier to understand the flow of the app. If the `BarPage` for example wants
+to link to an external page in the header, it might declare an
+`ExternalRouteRef` similar to this:
+
+```ts
+const headerLinkRouteRef = createExternalRouteRef();
+```
+
+### Binding External Routes in the App
+
+The association of external routes are controlled by the app. Each
+`ExternalRouteRef` of a plugin is bound to an actual `RouteRef`, usually from
+another plugin. The binding process happens once att app startup, and is then
+used through the lifetime of the app to help resolve concrete route paths.
+
+Using the above example of the `BarPage` linking to the `FooPage`, we might do
+something like this in the app:
+
+```ts
+createApp({
+ bindRoutes({ bind }) {
+ bind(barPlugin.externalRoutes, {
+ headerLink: fooPlugin.routes.root,
+ });
+ },
+});
+```
+
+Given the above binding, using `useRouteRef(external)`
+
+Note that we are not importing and using the `RouteRef`s directly, and instead
+rely on the plugin instance to access routes of the plugins. This is a new
+convention that was introduced to provide better namespacing and discoverability
+of routes, as well as reduce the number of different things exported from each
+plugin package. The route references would be supplied to `createPlugin` like
+this:
+
+```ts
+// In foo-plugin
+export const fooPlugin = createPlugin({
+ routes: {
+ root: fooRootRouteRef,
+ },
+ ...
+})
+
+// In bar-plugin
+export const barPlugin = createPlugin({
+ externalRoutes: {
+ headerLink: headerLinkRouteRef,
+ },
+ ...
+})
+```
+
+### New Catalog Components
+
+EntitySwitch & EntityLayout, and how to use those in the app
TODO
-## RouteRefs, useRouteRef, and plugin routes and externalRoutes
+## Porting Existing Plugins
-TODO
-
-## Binding external routes in the app
-
-TODO
-
-## New catalog components, EntitySwitch & EntityLayout, and how to use those in the app
-
-TODO
+## Porting Existing Apps
From 70d8653bdad5649399bcae8d146922bdc3d7b2d5 Mon Sep 17 00:00:00 2001
From: Patrik Oldsberg
Date: Wed, 13 Jan 2021 23:52:10 +0100
Subject: [PATCH 47/92] docs/composability: the rest of the f***ing owl
---
docs/plugins/composability.md | 403 +++++++++++++++++++++++++++++-----
1 file changed, 346 insertions(+), 57 deletions(-)
diff --git a/docs/plugins/composability.md b/docs/plugins/composability.md
index 5c96b88cee..3e38f3376e 100644
--- a/docs/plugins/composability.md
+++ b/docs/plugins/composability.md
@@ -16,15 +16,17 @@ composability system.
The core principle of the new composability system is that plugins should have
clear boundaries and connections. It should isolate crashes within a plugin, but
allow navigation between them. It should allow for plugins to be loaded only
-when needed, and enable plugins to provide extension point for other plugins to
+when needed, and enable plugins to provide extension points for other plugins to
build upon. The composability system is also built with an app-first mindset,
-prioritizing simplicity and clarity in the app over plugins and core APIs.
+prioritizing simplicity and clarity in the app over that in the plugins and core
+APIs.
The new composability system isn't a single new API surface. It is a collection
-of patterns, new primitives, new APIs, and old APIs used in new ways. At the
-core is the new concept of Extensions, are exported by plugins for use in the
-app. There is a new primitive called component data, which is used to connect
-plugin and the app, and a new hook that provides a practical use of .
+of patterns, primitives, new APIs, and old APIs used in new ways. At the core is
+the new concept of extensions, which are exported by plugins for use in the app.
+There is also a new primitive called component data, which assists in the
+conversion to a more declarative app. The `RouteRef`s now have a clear purpose
+as well, and can be used route to pages in a flexible way.
## New Concepts
@@ -36,7 +38,7 @@ in place to support the new composability system.
Component data is a new composability primitive that is introduced as a way to
provide a new data dimension for React components. Data is attached to React
components using a key, and is then readable from any JSX elements created with
-those components using the same key, as illustrated by the following example:
+those components, using the same key, as illustrated by the following example:
```tsx
const MyComponent = () =>
This is my component
;
@@ -51,26 +53,26 @@ The purpose of component data is to provide a method for embedding data that can
be inspected before rendering elements. Element inspection is a pattern that is
quite common among React libraries, and used for example by `react-router` and
`material-ui` to discover properties of the child elements before rendering.
-Though in those libraries only the element type and props are typically
+Although in those libraries only the element type and props are typically
inspected, while our component data adds more structured access and simplifies
evolution by allowing for multiple different versions of a piece of data to be
-used at once.
+used and interpreted at once.
The initial use-cases for component data is support route and plugin discovery
through elements in the app. Through this we allow for the React element tree in
-the app to be the source of truth, both for which plugins are used and all
-top-level plugin routes in the app. The use of component data is not limited to
-these use-cases though, as it can be used as a primitive to create new
+the app to be the source of truth, both for which plugins are used, as well as
+all top-level plugin routes in the app. The use of component data is not limited
+to these use-cases though, as it can be used as a primitive to create new
abstractions as well.
### Extensions
Extensions are what plugins export for use in an app. Most typically they are
-React components, but in practice they can be any kind of value. They are
-created using `create*Extension` functions, and wrapped with `plugin.provide()`
-in order to create the actual exported extension.
+React components, but in practice they can be any kind of JavaScript value. They
+are created using `create*Extension` functions, and wrapped with
+`plugin.provide()` in order to create the actual exported extension.
-The extension type is dead simple:
+The extension type is a simple one:
```ts
export type Extension = {
@@ -86,14 +88,14 @@ usage of the extension.
The Backstage core API currently provides two different types of extension
creators, `createComponentExtension`, and `createRoutableExtension`. Component
-extensions are plain react component with no particular requirements, such as
-cards for entity overview pages. The component will be exported more or less as
-is, but is wrapped up to provide things like an error boundary, lazy loading,
-and a plugin context.
+extensions are plain React component with no particular requirements, for
+example a card for an entity overview page. The component will be exported more
+or less as is, but is wrapped to provide things like an error boundary, lazy
+loading, and a plugin context.
Routable extensions build on top of component extensions and are used for any
-component that should be rendered at a specific route path, such as full pages
-or entity page tab content. When creating a routable extension you need to
+component that should be rendered at a specific route path, such as top-level
+pages or entity page tab content. When creating a routable extension you need to
supply a `RouteRef` as `mountPoint`. The mount point will be the handle of the
component for the outside world, and is used by other components and plugins
that wish to link to the routable component.
@@ -129,7 +131,8 @@ export const EntityFooCard = plugin.provide(
);
```
-Routable extensions even enforce lazy loading, for example:
+Routable extensions even enforce lazy loading, as it is the only way to provide
+a component:
```ts
export const FooPage = plugin.provide(
@@ -144,8 +147,8 @@ export const FooPage = plugin.provide(
Right now all extensions are modelled as React components. The usage of these
extension is like regular usage of any React components, with one important
-difference. Extensions must be all be part of a single React element tree
-spanning from the root `AppProvider`.
+difference. Extensions must all be part of a single React element tree spanning
+from the root `AppProvider`.
For example, the following app code does **NOT** work:
@@ -168,8 +171,8 @@ const App = () => (
);
```
-But it is simple to fix! Simply make sure that you don't create any intermediate
-components in the app, for example like this:
+But in this case it is simple to fix! Simply be sure to not create any
+intermediate components in the app, for example like this:
```tsx
const appRoutes = (
@@ -198,10 +201,12 @@ concrete `path` for each `RouteRef` is discovered based on the element tree in
the app. Let's consider the following example:
```tsx
-
- } />
- } />
-
+const appRoutes = (
+
+ } />
+ } />
+
+);
```
We'll assume that `FooPage` and `BarPage` are routable extensions, exported by
@@ -214,24 +219,32 @@ Given the above example, the `fooRootRouteRef` will be associated with the
`RouteRef` though, as the routing structure is tied to the app's react tree. We
instead use the new `useRouteRef` hook if we want to create a concrete link to
the page. The `useRouteRef` hook takes a single `RouteRef` as its only
-parameter, and returns a function that is called to create the URL.
+parameter, and returns a function that is called to create the URL. For example
+like this:
+
+```tsx
+const MyComponent = () => {
+ const fooRoute = useRouteRef(fooRouteRef);
+ return Link to Foo;
+};
+```
Now let's assume that we want to link from the `BarPage` to the `FooPage`.
Before the introduction of the new composability system, we would do this by
importing the `fooRootRouteRef` from the `fooPlugin`. This created an
-unnecessary dependency on the plugin, and also provided little flexibility
-allowing the app to tie plugins together rather than the plugins themselves. To
-handle this, we introduce the concept of `ExternalRouteRef`s. Much like regular
-route refs, they can be passed to `useRouteRef` to create concrete URLs, but
-they can not be used as mount points in routable component and instead have to
-be associated with an actual using route bindings in the app.
+unnecessary dependency on the plugin, and also provided little flexibility in
+allowing the app to tie plugins together, with the links instead being dictated
+by the plugins themselves. To solve this, we introduce `ExternalRouteRef`s. Much
+like regular route references, they can be passed to `useRouteRef` to create
+concrete URLs, but they can not be used as mount points in routable component
+and instead have to be associated with a target route using route bindings in
+the app.
-The `ExternalRouteRef` inside the `barPlugin` should also not be opinionated
-about what it is linking to either, allowing the app to decide the final target.
-It should however provide context in how the link is presented or used, to make
-it easier to understand the flow of the app. If the `BarPage` for example wants
-to link to an external page in the header, it might declare an
-`ExternalRouteRef` similar to this:
+We create a new `ExternalRouteRef` inside the `barPlugin`, using a neutral name
+that describes its role in the plugin rather than a specific plugin page that it
+might be linking to, allowing the app to decide the final target. If the
+`BarPage` for example wants to link to an external page in the header, it might
+declare an `ExternalRouteRef` similar to this:
```ts
const headerLinkRouteRef = createExternalRouteRef();
@@ -239,10 +252,11 @@ const headerLinkRouteRef = createExternalRouteRef();
### Binding External Routes in the App
-The association of external routes are controlled by the app. Each
-`ExternalRouteRef` of a plugin is bound to an actual `RouteRef`, usually from
-another plugin. The binding process happens once att app startup, and is then
-used through the lifetime of the app to help resolve concrete route paths.
+The association of external routes is controlled by the app. Each
+`ExternalRouteRef` of a plugin should be<- bound to an actual `RouteRef`,
+usually from another plugin. The binding process happens once att app startup,
+and is then used through the lifetime of the app to help resolve concrete route
+paths.
Using the above example of the `BarPage` linking to the `FooPage`, we might do
something like this in the app:
@@ -257,14 +271,15 @@ createApp({
});
```
-Given the above binding, using `useRouteRef(external)`
+Given the above binding, using `useRouteRef(headerLinkRouteRef)` within the
+`barPlugin` will let us create a link whatever path the `FooPage` is mounted at.
-Note that we are not importing and using the `RouteRef`s directly, and instead
-rely on the plugin instance to access routes of the plugins. This is a new
-convention that was introduced to provide better namespacing and discoverability
-of routes, as well as reduce the number of different things exported from each
-plugin package. The route references would be supplied to `createPlugin` like
-this:
+Note that we are not importing and using the `RouteRef`s directly in the app,
+and instead rely on the plugin instance to access routes of the plugins. This is
+a new convention that was introduced to provide better namespacing and
+discoverability of routes, as well as reduce the number of separate exports from
+each plugin package. The route references would be supplied to `createPlugin`
+like this:
```ts
// In foo-plugin
@@ -284,12 +299,286 @@ export const barPlugin = createPlugin({
})
```
+Also note that you almost always want to create the route references themselves
+in a different file than the one that creates the plugin instance, for example a
+top-level `routes.ts`. This is to avoid circular imports when you use the route
+references from other parts of the app.
+
+### Parameterized Routes
+
+A new addition to `RouteRef`s is the possibility of adding named and typed
+parameters. Parameters are declared at creation, and will enforce presence of
+the parameters in the path in the app, and require them as a parameter when
+using `useRouteRef`.
+
+The following is an example of creation and usage of a parameterized route:
+
+```tsx
+// Creation of a parameterized route
+const myRouteRef = createRouteRef({
+ title: 'My Named Route',
+ params: ['name']
+})
+
+// In the app, where MyPage is a routable extension with myRouteRef set as mountPoint
+}/>
+
+// Usage within a component
+const myRoute = useRouteRef(myRouteRef)
+return (
+
+)
+```
+
+It is currently not possible to have parameterized `ExternalRouteRef`s, or to
+bind an external route to a parameterized route, although this may be added in
+the future if needed.
+
### New Catalog Components
-EntitySwitch & EntityLayout, and how to use those in the app
+The established pattern for selecting what plugins should be available on each
+catalog page is to use custom components in the app, with logic embedded in the
+render function. Typically this takes form as a component that either receives
+the entity via props or uses the `useEntity` hook to retrieve the selected
+entity. A `switch` or `if` / `else if` chain is then used to select what
+children should be rendered based on information in the entity.
-TODO
+This pattern will no longer work with the new composability system, and in
+general is very difficult to build any form declarative model around, as it
+depends on runtime execution. To help replace existing code, a new
+`EntitySwitch` component has been added to the `@backstage/catalog` plugin,
+which grabs the selected entity from context, and selects at most one element to
+render using a list of `EntitySwitch.Case`s children.
+
+For example, if you want all entities of kind `"Template"` to be rendered with a
+`MyTemplate` component, and all other entities to be rendered with a `MyOther`
+component, you would do the following:
+
+```tsx
+
+
+
+
+
+
+
+
+
+
+// Shorter form if desired:
+
+ }/>
+ }/>
+
+```
+
+The `EntitySwitch` component will render the children of the first
+`EntitySwitch.Case` that returns `true` when the selected entity is passed to
+the function of the `if` prop. If none of the cases match, no children will be
+rendered, and if a case doesn't specify an `if` filter function, it will always
+match. The `if` property is simply a function of the type
+`(entity: Entity) => boolean`, for example, `isKind` can be implemented like
+this:
+
+```ts
+function isKind(kind: string) {
+ return (entity: Entity) => entity.kind.toLowerCase() === kind.toLowerCase();
+}
+```
+
+The `@backstage/catalog` plugin provides a couple of built-in conditions,
+`isKind`, `isComponentType`, and `isNamespace`.
+
+In addition to the `EntitySwitch` component, the catalog plugin also exports a
+new `EntityLayout` component. It is a tweaked version and replacement for the
+`EntityPageLayout` component, and is introduced more in depth in the app
+migration section below.
## Porting Existing Plugins
+There are a couple of high-level steps to porting an existing plugin to the new
+composability system:
+
+- Remove usage of `router.addRoute` or `router.registerRoute` within
+ `createPlugin`, and export the page components as routable extensions instead.
+- Switch any `Router` export to instead be a routable extension.
+- Change any plain component exports, such as catalog overview cards, to be
+ component extensions.
+- Stop exporting `RouteRef`s and instead pass them to `createPlugin`.
+- Stop accepting `RouteRef`s as props or importing them from other plugins,
+ instead create an `ExternalRouteRef` as a replacement, and pass it to
+ `createPlugin.`
+- Rename any other exported symbols according to the naming pattern table below.
+
+Note that removing the existing exports and configuration is a breaking change
+in any plugin. If backwards compatibility is needed the existing code be
+deprecated while making the new additions, to then be removed at a later point.
+
+### Naming Patterns
+
+Many export naming patterns have been changed to avoid import aliases and to
+clarify intent. Refer to the following table to formulate the new name:
+
+| Description | Existing Pattern | New Pattern | Examples |
+| -------------------- | -------------------------- | --------------- | ---------------------------------------------- |
+| Top-level Pages | Router | \*Page | CatalogIndexPage, SettingsPage, LighthousePage |
+| Entity Tab Content | Router | Entity\*Content | EntityJenkinsContent, EntityKubernetesContent |
+| Entity Overview Card | \*Card | Entity\*Card | EntitySentryCard, EntityPagerDutyCard |
+| Entity Conditional | isPluginApplicableToEntity | is\*Available | isPagerDutyAvailable, isJenkinsAvailable |
+| Plugin Instance | plugin | \*Plugin | jenkinsPlugin, catalogPlugin |
+
## Porting Existing Apps
+
+The first step of porting any app is to replace the root `Routes` component with
+`FlatRoutes` from `@backstage/core`. As opposed to the `Routes` component,
+`FlatRoutes` only considers the first level of `Route` components in its
+children, and provides any additional children to the outlet of the route. It
+also removes the need to append `"/*"` to paths, as it is added automatically.
+
+```diff
+const AppRoutes = () => (
+-
++
+ ...
+- } />
++ } />
+ ...
+-
++
+);
+```
+
+The next step should be to switch from using `EntityPageLayout` to
+`EntityLayout`, as this can also be done without waiting for plugins to be
+ported. You should also replace the top-level `Router` from the catalog plugin
+with the separate `CatalogIndexPage` and `CatalogEntityPage` extensions that
+have been added to the catalog:
+
+```diff
+-}
+-/>
++} />
++}
++>
++
++
+```
+
+At that point you should flatten out the element tree as much as possible in the
+app, removing any intermediate components. At the top level this should usually
+be straightforward, but when reaching the catalog entity pages you may need to
+wait for some plugins to be migrated. This is because it is no longer possible
+to pass in the selected entity through component props, and it should be picked
+up from context inside the plugin instead. See the sections below for how to
+carry out migrations of some common entity page patterns.
+
+Once the app element tree doesn't contain any intermediate components, and all
+plugin imports have been switched to extensions rather than plain components,
+the app has been fully ported.
+
+### Switching from EntityPageLayout to EntityLayout
+
+The existing `EntityPageLayout` is replaced by the new `EntityLayout` component,
+which has a slightly different pattern for expressing the contents and paths.
+
+Porting from the old to the new API is just a matter of moving some things
+around. For example, given the following existing code:
+
+```tsx
+
+ }
+ />
+ }
+ />
+ }
+ />
+
+```
+
+It would be ported to this:
+
+```tsx
+
+
+ }
+
+
+
+ }
+
+
+
+ }
+
+
+```
+
+In addition to the renaming, the `element` prop has been moved to `children`.
+Also note that the `/*` suffix has been remove from the `"/kubernetes"` path, as
+it's now added automatically.
+
+Usage of the `EntityLayout` component is required to be able to properly
+discover routes, and so it is required to apply this change before you can start
+using routable entity content extensions from plugins.
+
+### Porting Entity Pages
+
+The established pattern in the app is to use custom components in order to
+select what plugin components to render for a given entity. The new
+`EntitySwitch` component introduced above is what is intended to replace this
+pattern, now that the entire app needs to be rendered as a single element tree.
+For example, given the following existing code:
+
+```tsx
+export const EntityPage = () => {
+ const { entity } = useEntity();
+
+ switch (entity?.kind?.toLowerCase()) {
+ case 'component':
+ return ;
+ case 'api':
+ return ;
+ case 'group':
+ return ;
+ case 'user':
+ return ;
+ default:
+ return ;
+ }
+};
+```
+
+It would be migrated to this:
+
+```tsx
+export const entityPage = (
+
+
+
+
+
+
+
+);
+```
+
+Note that for example `` has been changed to simply
+`componentPage`, that is because just like the `EntityPage` component, the
+`ComponentEntityPage` also needs to be ported to be an element rather a
+component in a similar way.
From 72ce0f8b163b4d01a309064cad99f6f5af20e36e Mon Sep 17 00:00:00 2001
From: Patrik Oldsberg
Date: Thu, 14 Jan 2021 02:04:57 +0100
Subject: [PATCH 48/92] docs/composability: include in sidebar and add note
about purpose
---
docs/plugins/composability.md | 11 ++++++-----
microsite/sidebars.json | 1 +
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/docs/plugins/composability.md b/docs/plugins/composability.md
index 3e38f3376e..e6f00d2374 100644
--- a/docs/plugins/composability.md
+++ b/docs/plugins/composability.md
@@ -1,6 +1,6 @@
---
id: composability
-title: New Composability System
+title: Composability System Migration
description:
Documentation and migration instructions for new composability APIs.
---
@@ -8,10 +8,11 @@ description:
## Summary
This page describes the new composability system that was recently introduced in
-Backstage. It describes the new system from the perspective of the existing
-patterns and APIs. As the new system is solidified and existing code is ported,
-this page will removed and replaced with a more direct description of the
-composability system.
+Backstage, and it does so from the perspective of the existing patterns and
+APIs. As the new system is solidified and existing code is ported, this page
+will removed and replaced with a more direct description of the composability
+system. For now, the primary purpose of this documentation is to aid in the
+migration of existing plugins, but it does cover the migration of apps as well.
The core principle of the new composability system is that plugins should have
clear boundaries and connections. It should isolate crashes within a plugin, but
diff --git a/microsite/sidebars.json b/microsite/sidebars.json
index 3d6417cc65..05e421cb19 100644
--- a/microsite/sidebars.json
+++ b/microsite/sidebars.json
@@ -95,6 +95,7 @@
"plugins/plugin-development",
"plugins/structure-of-a-plugin",
"plugins/integrating-plugin-into-service-catalog",
+ "plugins/composability",
{
"type": "subcategory",
"label": "Backends and APIs",
From 1f383bbb7c07e5b5e2f7892731bb0c68fc79e749 Mon Sep 17 00:00:00 2001
From: Adam Harvey
Date: Thu, 14 Jan 2021 00:24:40 -0500
Subject: [PATCH 49/92] Clarity of error vs problem
---
.../src/components/KubernetesContent/ErrorPanel.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/plugins/kubernetes/src/components/KubernetesContent/ErrorPanel.tsx b/plugins/kubernetes/src/components/KubernetesContent/ErrorPanel.tsx
index 85060fc11b..650e6ddff1 100644
--- a/plugins/kubernetes/src/components/KubernetesContent/ErrorPanel.tsx
+++ b/plugins/kubernetes/src/components/KubernetesContent/ErrorPanel.tsx
@@ -52,8 +52,8 @@ export const ErrorPanel = ({
clustersWithErrors,
}: ErrorPanelProps) => (
{clustersWithErrors && (