Merge pull request #21 from backstage/master

Upstream update
This commit is contained in:
Spencer Kaiser
2021-06-04 17:08:12 -05:00
committed by GitHub
205 changed files with 4374 additions and 1240 deletions
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/test-utils': patch
---
Fix a bug in `MockStorageApi` where it unhelpfully returned new empty buckets every single time
-32
View File
@@ -1,32 +0,0 @@
---
'@backstage/plugin-scaffolder': patch
---
Scaffolder Field Extensions are here! This means you'll now the ability to create custom field extensions and have the Scaffolder use the components when collecting information from the user in the wizard. By default we supply the `RepoUrlPicker` and the `OwnerPicker`, but if you want to provide some more extensions or override the built on ones you will have to change how the `ScaffolderPage` is wired up in your `app/src/App.tsx` to pass in the custom fields to the Scaffolder.
You'll need to move this:
```tsx
<Route path="/create" element={<ScaffolderPage />} />
```
To this:
```tsx
import {
ScaffolderFieldExtensions,
RepoUrlPickerFieldExtension,
OwnerPickerFieldExtension,
} from '@backstage/plugin-scaffolder';
<Route path="/create" element={<ScaffolderPage />}>
<ScaffolderFieldExtensions>
<RepoUrlPickerFieldExtension />
<OwnerPickerFieldExtension />
{/*Any other extensions you want to provide*/}
</ScaffolderFieldExtensions>
</Route>;
```
More documentation on how to write your own `FieldExtensions` to follow.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/cli': patch
---
Adjust the Webpack `devtool` module filename template to correctly resolve via the source maps to the source files.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog': patch
---
A `<CatalogResultListItem />` component is now available for use in custom Search Experiences.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-cost-insights': patch
---
Move `canvas` package to `devDependencies`.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-scaffolder': patch
---
Remove the trailing space from a the aria-label of the Template "CHOOSE" button.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/cli': patch
---
Exclude core packages from package dependency diff.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-scaffolder': patch
---
Don't merge with previous from state on form changes.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend': patch
---
Restructure the next catalog types and files a bit
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-catalog': patch
---
invert logic for when to show type column
-11
View File
@@ -1,11 +0,0 @@
---
'@backstage/cli': patch
'@backstage/core-app-api': patch
'@backstage/core-components': patch
'@backstage/core-plugin-api': patch
'@backstage/dev-utils': patch
'@backstage/test-utils': patch
'@backstage/theme': patch
---
Update installation instructions in README.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/catalog-model': patch
---
Remove the explicit connection from `EntityEnvelope` and `Entity`.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/integration': patch
---
GitHub App ID can be a string too for environment variables otherwise it will fail validation
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Updated dependencies
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/core': patch
---
Export `CheckboxTree` as we have a storybook for it
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-catalog-react': patch
---
Sort `EntityTagPicker` entries.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-scaffolder': patch
---
Add a `<EntityPicker>` field to the scaffolder to pick arbitrary entity kinds, like systems.
+9
View File
@@ -0,0 +1,9 @@
---
'@backstage/search-common': patch
'@backstage/plugin-search-backend-node': patch
'@backstage/plugin-search': patch
---
The `<Search...Next /> set of components exported by the Search Plugin are now updated to use the Search Backend API. These will be made available as the default non-"next" versions in a follow-up release.
The interfaces for decorators and collators in the Search Backend have also seen minor, breaking revisions ahead of a general release. If you happen to be building on top of these interfaces, check and update your implementations accordingly. The APIs will be considered more stable in a follow-up release.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-scaffolder-backend': patch
---
This adds a configuration option to the scaffolder plugin router, so we can allow for multiple `TaskWorkers`. Currently with only one `TaskWorker` you are limited to scaffolding one thing at a time. Set the `taskWorkers?: number` option in your scaffolder router to get more than 1 `TaskWorker`
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/create-app': patch
---
Add CLI output and README how to start app after create-app CLI
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-catalog-react': patch
---
Tweak the `EntityListProvider` to do single-cycle updates
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/cli': patch
---
chore(deps): bump `@spotify/eslint-config-react` from 9.0.0 to 10.0.0
-6
View File
@@ -1,6 +0,0 @@
---
'@backstage/techdocs-common': patch
---
Support parsing `mkdocs.yml` files that are using custom yaml tags like
`!!python/name:materialx.emoji.twemoji`.
-7
View File
@@ -1,7 +0,0 @@
---
'@backstage/plugin-catalog': patch
'@backstage/plugin-catalog-backend': patch
---
Move dependency to `@microsoft/microsoft-graph-types` from `@backstage/plugin-catalog`
to `@backstage/plugin-catalog-backend`.
+1
View File
@@ -210,6 +210,7 @@ rst
rsync
ruleset
sam
sanitization
scaffolded
scaffolder
Scaffolder
+3
View File
@@ -76,6 +76,9 @@ jobs:
- name: prettier
run: yarn prettier:check
- name: lock
run: yarn lock:check
- name: validate config
run: yarn backstage-cli config:check --lax
@@ -40,6 +40,10 @@ jobs:
run: yarn prettier:check
working-directory: microsite
- name: lock
run: yarn lock:check
working-directory: microsite
- name: build microsite
run: yarn build
working-directory: microsite
+2 -1
View File
@@ -23,7 +23,8 @@ Backstage provides a utility for creating new apps. It guides you through the
initial setup of selecting the name of the app and a database for the backend.
The database options are either SQLite or PostgreSQL, where the latter requires
you to set up a separate database instance. If in doubt, choose SQLite, but
don't worry about the choice, it's easy to change later!
don't worry about the choice, it's easy to change later! Here is a
[tutorial](../tutorials/switching-sqlite-postgres.md) for it.
The easiest way to run the create app package is with `npx`:
+13
View File
@@ -0,0 +1,13 @@
---
title: Cortex Service Quality Scorecards
author: Cortex
authorUrl: https://www.getcortexapp.com
category: Monitoring
description: Grade the quality of your Backstage services using Scorecards. Automate production readiness, migrations, security audits, and more with CQL (Cortex Query Language).
documentation: https://github.com/cortexapps/backstage-plugin
iconUrl: img/cortex.png
npmPackageName: '@cortexapps/backstage-plugin'
tags:
- web
- monitoring
- sre
+4 -2
View File
@@ -12,13 +12,15 @@
"write-translations": "docusaurus-write-translations",
"version": "docusaurus-version",
"rename-version": "docusaurus-rename-version",
"verify:sidebars": "node ./scripts/verify-sidebars"
"verify:sidebars": "node ./scripts/verify-sidebars",
"lock:check": "yarn-lock-check"
},
"devDependencies": {
"@spotify/prettier-config": "^10.0.0",
"docusaurus": "^2.0.0-alpha.70",
"js-yaml": "^4.1.0",
"prettier": "^2.3.0"
"prettier": "^2.3.0",
"yarn-lock-check": "^1.0.4"
},
"prettier": "@spotify/prettier-config"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

+138 -8
View File
@@ -9,6 +9,13 @@
dependencies:
"@babel/highlight" "^7.0.0"
"@babel/code-frame@^7.0.0":
version "7.12.13"
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658"
integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==
dependencies:
"@babel/highlight" "^7.12.13"
"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.11":
version "7.12.11"
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
@@ -220,6 +227,11 @@
resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed"
integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==
"@babel/helper-validator-identifier@^7.14.0":
version "7.14.0"
resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288"
integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==
"@babel/helper-validator-option@^7.12.1", "@babel/helper-validator-option@^7.12.11":
version "7.12.11"
resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz#d66cb8b7a3e7fe4c6962b32020a131ecf0847f4f"
@@ -253,6 +265,15 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
"@babel/highlight@^7.12.13":
version "7.14.0"
resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf"
integrity sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==
dependencies:
"@babel/helper-validator-identifier" "^7.14.0"
chalk "^2.0.0"
js-tokens "^4.0.0"
"@babel/parser@^7.12.10", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7":
version "7.12.11"
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz#9ce3595bcd74bc5c466905e86c535b8b25011e79"
@@ -911,7 +932,7 @@
"@spotify/prettier-config@^10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@spotify/prettier-config/-/prettier-config-10.0.0.tgz#fa076d98d2e7e6c53dd3d86a696307a7010bd056"
resolved "https://registry.npmjs.org/@spotify/prettier-config/-/prettier-config-10.0.0.tgz#fa076d98d2e7e6c53dd3d86a696307a7010bd056"
integrity sha512-VYOdo8P7lIScAkl02nB9KpUAuOYMManryBIBuKJkAw5D3aVtLobfmdIKvdV6MqEmGMEQPbn7w/UpnjJYhUH+IA==
"@types/cheerio@^0.22.8":
@@ -921,16 +942,39 @@
dependencies:
"@types/node" "*"
"@types/glob@^7.1.3":
version "7.1.3"
resolved "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==
dependencies:
"@types/minimatch" "*"
"@types/node" "*"
"@types/minimatch@*":
version "3.0.4"
resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21"
integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==
"@types/node@*":
version "14.14.20"
resolved "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz#f7974863edd21d1f8a494a73e8e2b3658615c340"
integrity sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==
"@types/node@^15.6.1":
version "15.9.0"
resolved "https://registry.npmjs.org/@types/node/-/node-15.9.0.tgz#0b7f6c33ca5618fe329a9d832b478b4964d325a8"
integrity sha512-AR1Vq1Ei1GaA5FjKL5PBqblTZsL5M+monvGSZwe6sSIdGiuu7Xr/pNwWJY+0ZQuN8AapD/XMB5IzBAyYRFbocA==
"@types/q@^1.5.1":
version "1.5.4"
resolved "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
"@yarnpkg/lockfile@^1.1.0":
version "1.1.0"
resolved "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==
accepts@~1.3.7:
version "1.3.7"
resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
@@ -1039,7 +1083,7 @@ argparse@^1.0.10, argparse@^1.0.7:
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
arr-diff@^4.0.0:
@@ -1405,6 +1449,11 @@ buffer@^5.2.1:
base64-js "^1.3.1"
ieee754 "^1.1.13"
builtin-modules@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=
bytes@1:
version "1.0.0"
resolved "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz#3569ede8ba34315fab99c3e92cb04c7220de1fa8"
@@ -1518,7 +1567,7 @@ caw@^2.0.0, caw@^2.0.1:
tunnel-agent "^0.6.0"
url-to-options "^1.0.1"
chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2:
chalk@2.4.2, chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -1709,7 +1758,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
dependencies:
delayed-stream "~1.0.0"
commander@^2.8.1:
commander@^2.12.1, commander@^2.8.1:
version "2.20.3"
resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
@@ -2181,6 +2230,11 @@ diacritics-map@^0.1.0:
resolved "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz#6dfc0ff9d01000a2edf2865371cac316e94977af"
integrity sha1-bfwP+dAQAKLt8oZTccrDFulJd68=
diff@^4.0.1:
version "4.0.2"
resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
dir-glob@2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034"
@@ -3049,6 +3103,18 @@ glob@^7.0.0, glob@^7.0.5, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@~7.1.1:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.1.1, glob@^7.1.7:
version "7.1.7"
resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
global-modules@2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780"
@@ -3460,6 +3526,11 @@ ini@^1.3.4, ini@^1.3.5:
resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
ini@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==
inquirer@6.5.0:
version "6.5.0"
resolved "https://registry.npmjs.org/inquirer/-/inquirer-6.5.0.tgz#2303317efc9a4ea7ec2e2df6f86569b734accf42"
@@ -3567,6 +3638,13 @@ is-core-module@^2.1.0:
dependencies:
has "^1.0.3"
is-core-module@^2.2.0:
version "2.4.0"
resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1"
integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==
dependencies:
has "^1.0.3"
is-data-descriptor@^0.1.4:
version "0.1.4"
resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
@@ -3864,7 +3942,7 @@ js-yaml@^3.13.1, js-yaml@^3.8.1:
js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
dependencies:
argparse "^2.0.1"
@@ -4379,7 +4457,7 @@ mixin-deep@^1.1.3, mixin-deep@^1.2.0:
for-in "^1.0.2"
is-extendable "^1.0.1"
mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1:
mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1:
version "0.5.5"
resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
@@ -5199,7 +5277,7 @@ prepend-http@^2.0.0:
prettier@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18"
resolved "https://registry.npmjs.org/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18"
integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==
prismjs@^1.22.0:
@@ -5603,6 +5681,14 @@ resolve@^1.1.6, resolve@^1.10.0:
is-core-module "^2.1.0"
path-parse "^1.0.6"
resolve@^1.3.2:
version "1.20.0"
resolved "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
dependencies:
is-core-module "^2.2.0"
path-parse "^1.0.6"
responselike@1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"
@@ -6351,11 +6437,37 @@ truncate-html@^1.0.3:
"@types/cheerio" "^0.22.8"
cheerio "0.22.0"
tslib@^1.9.0, tslib@^1.9.3:
tslib@^1.13.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
version "1.14.1"
resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslint@^6.1.3:
version "6.1.3"
resolved "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz#5c23b2eccc32487d5523bd3a470e9aa31789d904"
integrity sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==
dependencies:
"@babel/code-frame" "^7.0.0"
builtin-modules "^1.1.1"
chalk "^2.3.0"
commander "^2.12.1"
diff "^4.0.1"
glob "^7.1.1"
js-yaml "^3.13.1"
minimatch "^3.0.4"
mkdirp "^0.5.3"
resolve "^1.3.2"
semver "^5.3.0"
tslib "^1.13.0"
tsutils "^2.29.0"
tsutils@^2.29.0:
version "2.29.0"
resolved "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99"
integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==
dependencies:
tslib "^1.8.1"
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
@@ -6381,6 +6493,11 @@ typedarray@^0.0.6:
resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@^4.3.2:
version "4.3.2"
resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805"
integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==
unbzip2-stream@^1.0.9:
version "1.4.3"
resolved "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7"
@@ -6650,6 +6767,19 @@ yargs@^2.3.0:
dependencies:
wordwrap "0.0.2"
yarn-lock-check@^1.0.4:
version "1.0.4"
resolved "https://registry.npmjs.org/yarn-lock-check/-/yarn-lock-check-1.0.4.tgz#a0373de051be0c8442d8933070df7a45595263b4"
integrity sha512-Gj0wRN85c4OPZUlE7WsQ0a1COv38uyeWWR0YAvJr2Vxw1f32bwK19xySjUZlxt9o4QopJkd8g6x6CLv81OHwYg==
dependencies:
"@types/glob" "^7.1.3"
"@types/node" "^15.6.1"
"@yarnpkg/lockfile" "^1.1.0"
glob "^7.1.7"
ini "^2.0.0"
tslint "^6.1.3"
typescript "^4.3.2"
yauzl@^2.4.2:
version "2.10.0"
resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
+5 -3
View File
@@ -31,7 +31,8 @@
"lerna": "lerna",
"storybook": "yarn workspace storybook start",
"build-storybook": "yarn workspace storybook build-storybook",
"prepare": "husky install"
"prepare": "husky install",
"lock:check": "yarn-lock-check"
},
"workspaces": {
"packages": [
@@ -56,7 +57,7 @@
"devDependencies": {
"@changesets/cli": "^2.14.0",
"@octokit/openapi-types": "^2.2.0",
"@spotify/prettier-config": "^9.0.0",
"@spotify/prettier-config": "^10.0.0",
"command-exists": "^1.2.9",
"concurrently": "^6.0.0",
"eslint-plugin-notice": "^0.9.10",
@@ -66,7 +67,8 @@
"lint-staged": "^10.1.0",
"prettier": "^2.2.1",
"recursive-readdir": "^2.2.2",
"shx": "^0.3.2"
"shx": "^0.3.2",
"yarn-lock-check": "^1.0.4"
},
"prettier": "@spotify/prettier-config",
"lint-staged": {
+33
View File
@@ -1,5 +1,38 @@
# example-app
## 0.2.31
### Patch Changes
- Updated dependencies [497f4ce18]
- Updated dependencies [ee4eb5b40]
- Updated dependencies [84160313e]
- Updated dependencies [3772de8ba]
- Updated dependencies [7e7c71417]
- Updated dependencies [f430b6c6f]
- Updated dependencies [2a942cc9e]
- Updated dependencies [e7c5e4b30]
- Updated dependencies [ebe802bc4]
- Updated dependencies [1cf1d351f]
- Updated dependencies [90a505a77]
- Updated dependencies [76f99a1a0]
- Updated dependencies [deaba2e13]
- Updated dependencies [1157fa307]
- Updated dependencies [8e919a6f8]
- Updated dependencies [2305ab8fc]
- Updated dependencies [054bcd029]
- Updated dependencies [aad98c544]
- Updated dependencies [f46a9e82d]
- @backstage/plugin-scaffolder@0.9.7
- @backstage/cli@0.6.14
- @backstage/plugin-catalog@0.6.1
- @backstage/theme@0.2.8
- @backstage/catalog-model@0.8.1
- @backstage/core@0.7.12
- @backstage/plugin-tech-radar@0.4.0
- @backstage/plugin-catalog-react@0.2.1
- @backstage/plugin-techdocs@0.9.5
## 0.2.30
### Patch Changes
@@ -0,0 +1,121 @@
/*
* Copyright 2021 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.
*/
const API_ENDPOINT = 'http://localhost:7000/api/search/query';
describe('SearchPage', () => {
describe('Given a search context with a term, results, and filter values', () => {
it('The results are rendered as expected', () => {
const results = [
{
type: 'software-catalog',
document: {
title: 'backstage',
text: 'Backstage system documentation',
location: '/result/location/path',
},
},
];
cy.enterAsGuest();
cy.visit('/search-next', {
onBeforeLoad(win) {
cy.stub(win, 'fetch')
.withArgs(`${API_ENDPOINT}?term=&pageCursor=`)
.resolves({
ok: true,
json: () => ({ results }),
});
},
});
cy.contains('Search');
cy.contains(results[0].document.title);
cy.contains(results[0].document.text);
cy.get(`a[href="${results[0].document.location}"]`).should('be.visible');
});
it('The filters are rendered as expected', () => {
cy.enterAsGuest();
cy.visit(
'/search-next?filters%5Bkind%5D=Component&filters%5Blifecycle%5D%5B%5D=experimental',
{
onBeforeLoad(win) {
cy.stub(win, 'fetch')
.withArgs(
`${API_ENDPOINT}?term=&filters%5Bkind%5D=Component&filters%5Blifecycle%5D%5B0%5D=experimental&pageCursor=`,
)
.resolves({
ok: true,
json: () => ({ results: [] }),
});
},
},
);
cy.contains('Search');
// lifecycle
cy.contains('lifecycle');
cy.contains('experimental');
cy.get(
'[data-testid="search-checkboxfilter-next"] input[value="experimental"]',
).should('have.attr', 'checked');
cy.contains('production');
cy.get(
'[data-testid="search-checkboxfilter-next"] input[value="production"]',
).should('not.have.attr', 'checked');
// kind
cy.contains('kind');
cy.get(
'[data-testid="search-selectfilter-next"] [role="button"][aria-haspopup="listbox"]',
).click();
cy.contains('All');
cy.contains('Template');
cy.contains('Component');
cy.get('[role="option"][data-value="Component"]').should(
'have.attr',
'aria-selected',
'true',
);
});
it('The search bar is rendered as expected', () => {
cy.enterAsGuest();
cy.visit('/search-next?query=backstage', {
onBeforeLoad(win) {
cy.stub(win, 'fetch')
.withArgs(`${API_ENDPOINT}?term=backstage&pageCursor=`)
.resolves({
ok: true,
json: () => ({ results: [] }),
});
},
});
cy.contains('Search');
cy.get('[data-testid="search-bar-next"] input').should(
'have.attr',
'value',
'backstage',
);
});
});
});
+19
View File
@@ -0,0 +1,19 @@
/*
* Copyright 2021 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.
*/
Cypress.Commands.add('enterAsGuest', () => {
cy.visit('/');
cy.get('button').contains('Enter').click();
});
+1
View File
@@ -14,3 +14,4 @@
* limitations under the License.
*/
import '@testing-library/cypress/add-commands';
import './commands';
+11 -11
View File
@@ -1,18 +1,18 @@
{
"name": "example-app",
"version": "0.2.30",
"version": "0.2.31",
"private": true,
"bundled": true,
"dependencies": {
"@backstage/catalog-model": "^0.8.0",
"@backstage/cli": "^0.6.13",
"@backstage/core": "^0.7.11",
"@backstage/catalog-model": "^0.8.1",
"@backstage/cli": "^0.6.14",
"@backstage/core": "^0.7.12",
"@backstage/integration-react": "^0.1.2",
"@backstage/plugin-api-docs": "^0.4.15",
"@backstage/plugin-badges": "^0.2.2",
"@backstage/plugin-catalog": "^0.6.0",
"@backstage/plugin-catalog": "^0.6.1",
"@backstage/plugin-catalog-import": "^0.5.8",
"@backstage/plugin-catalog-react": "^0.2.0",
"@backstage/plugin-catalog-react": "^0.2.1",
"@backstage/plugin-circleci": "^0.2.15",
"@backstage/plugin-cloudbuild": "^0.2.16",
"@backstage/plugin-code-coverage": "^0.1.4",
@@ -29,15 +29,15 @@
"@backstage/plugin-org": "^0.3.14",
"@backstage/plugin-pagerduty": "0.3.5",
"@backstage/plugin-rollbar": "^0.3.6",
"@backstage/plugin-scaffolder": "^0.9.6",
"@backstage/plugin-scaffolder": "^0.9.7",
"@backstage/plugin-search": "^0.3.7",
"@backstage/plugin-sentry": "^0.3.11",
"@backstage/plugin-shortcuts": "^0.1.2",
"@backstage/plugin-tech-radar": "^0.3.11",
"@backstage/plugin-techdocs": "^0.9.4",
"@backstage/plugin-tech-radar": "^0.4.0",
"@backstage/plugin-techdocs": "^0.9.5",
"@backstage/plugin-todo": "^0.1.2",
"@backstage/plugin-user-settings": "^0.2.10",
"@backstage/theme": "^0.2.7",
"@backstage/theme": "^0.2.8",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@octokit/rest": "^18.5.3",
@@ -56,7 +56,7 @@
"zen-observable": "^0.8.15"
},
"devDependencies": {
"@backstage/test-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.13",
"@testing-library/cypress": "^7.0.1",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
+4 -4
View File
@@ -54,6 +54,7 @@ import { Navigate, Route } from 'react-router';
import { apis } from './apis';
import { Root } from './components/Root';
import { entityPage } from './components/catalog/EntityPage';
import { searchPage } from './components/search/SearchPage';
import { providers } from './identityProviders';
import * as plugins from './plugins';
@@ -119,10 +120,9 @@ const routes = (
<Route path="/gcp-projects" element={<GcpProjectsPage />} />
<Route path="/newrelic" element={<NewRelicPage />} />
<Route path="/search" element={<SearchPage />} />
<Route
path="/search-next"
element={<SearchPageNext /* TODO: illustrate customization */ />}
/>
<Route path="/search-next" element={<SearchPageNext />}>
{searchPage}
</Route>
<Route path="/cost-insights" element={<CostInsightsPage />} />
<Route
path="/cost-insights/investigating-growth"
@@ -0,0 +1,102 @@
/*
* Copyright 2021 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 React from 'react';
import { makeStyles, Theme, Grid, List, Paper } from '@material-ui/core';
import { Content, Header, Lifecycle, Page } from '@backstage/core';
import { CatalogResultListItem } from '@backstage/plugin-catalog';
import {
SearchBarNext as SearchBar,
SearchFilterNext as SearchFilter,
SearchResultNext as SearchResult,
DefaultResultListItem,
} from '@backstage/plugin-search';
const useStyles = makeStyles((theme: Theme) => ({
bar: {
padding: theme.spacing(1, 0),
},
filters: {
padding: theme.spacing(2),
},
filter: {
'& + &': {
marginTop: theme.spacing(2.5),
},
},
}));
const SearchPage = () => {
const classes = useStyles();
return (
<Page themeId="home">
<Header title="Search" subtitle={<Lifecycle alpha />} />
<Content>
<Grid container direction="row">
<Grid item xs={12}>
<Paper className={classes.bar}>
<SearchBar debounceTime={100} />
</Paper>
</Grid>
<Grid item xs={3}>
<Paper className={classes.filters}>
<SearchFilter.Select
className={classes.filter}
name="kind"
values={['Component', 'Template']}
/>
<SearchFilter.Checkbox
className={classes.filter}
name="lifecycle"
values={['experimental', 'production']}
/>
</Paper>
</Grid>
<Grid item xs={9}>
<SearchResult>
{({ results }) => (
<List>
{results.map(({ type, document }) => {
switch (type) {
case 'software-catalog':
return (
<CatalogResultListItem
key={document.location}
result={document}
/>
);
default:
return (
<DefaultResultListItem
key={document.location}
result={document}
/>
);
}
})}
</List>
)}
</SearchResult>
</Grid>
</Grid>
</Content>
</Page>
);
};
export const searchPage = <SearchPage />;
-1
View File
@@ -30,7 +30,6 @@ export default async function createPlugin({
const indexBuilder = new IndexBuilder({ logger, searchEngine });
indexBuilder.addCollator({
type: 'software-catalog',
defaultRefreshIntervalSeconds: 600,
collator: new DefaultCatalogCollator({ discovery }),
});
+6
View File
@@ -1,5 +1,11 @@
# @backstage/catalog-model
## 0.8.1
### Patch Changes
- ebe802bc4: Remove the explicit connection from `EntityEnvelope` and `Entity`.
## 0.8.0
### Minor Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/catalog-model",
"version": "0.8.0",
"version": "0.8.1",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -41,7 +41,7 @@
"yup": "^0.29.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.13",
"@backstage/cli": "^0.6.14",
"@types/express": "^4.17.6",
"@types/jest": "^26.0.7",
"@types/lodash": "^4.14.151",
+11
View File
@@ -1,5 +1,16 @@
# @backstage/cli
## 0.6.14
### Patch Changes
- ee4eb5b40: Adjust the Webpack `devtool` module filename template to correctly resolve via the source maps to the source files.
- 84160313e: Mark the `create-github-app` command as ready for use and reveal it in the command list.
- 7e7c71417: Exclude core packages from package dependency diff.
- e7c5e4b30: Update installation instructions in README.
- 2305ab8fc: chore(deps): bump `@spotify/eslint-config-react` from 9.0.0 to 10.0.0
- 054bcd029: Deprecated the `backend:build-image` command, pointing to the newer `backend:bundle` command.
## 0.6.13
### Patch Changes
+6 -6
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/cli",
"description": "CLI for developing Backstage plugins and apps",
"version": "0.6.13",
"version": "0.6.14",
"private": false,
"publishConfig": {
"access": "public"
@@ -52,7 +52,7 @@
"@types/start-server-webpack-plugin": "^2.2.0",
"@types/webpack-env": "^1.15.2",
"@types/webpack-node-externals": "^2.5.0",
"@typescript-eslint/eslint-plugin": "^v4.15.2",
"@typescript-eslint/eslint-plugin": "^v4.26.0",
"@typescript-eslint/parser": "^v4.14.0",
"@yarnpkg/lockfile": "^1.1.0",
"babel-plugin-dynamic-import-node": "^2.3.3",
@@ -120,10 +120,10 @@
"devDependencies": {
"@backstage/backend-common": "^0.8.1",
"@backstage/config": "^0.1.5",
"@backstage/core": "^0.7.11",
"@backstage/dev-utils": "^0.1.16",
"@backstage/test-utils": "^0.1.12",
"@backstage/theme": "^0.2.7",
"@backstage/core": "^0.7.12",
"@backstage/dev-utils": "^0.1.17",
"@backstage/test-utils": "^0.1.13",
"@backstage/theme": "^0.2.8",
"@types/diff": "^5.0.0",
"@types/express": "^4.17.6",
"@types/fs-extra": "^9.0.1",
@@ -15,6 +15,7 @@
*/
import { Command } from 'commander';
import { yellow } from 'chalk';
import fs from 'fs-extra';
import { join as joinPath, relative as relativePath } from 'path';
import { createDistWorkspace } from '../../lib/packager';
@@ -31,6 +32,15 @@ export default async (cmd: Command) => {
return;
}
console.warn(
yellow(`
The backend:build-image command is deprecated and will be removed in the future.
Please use the backend:bundle command instead along with your own Docker setup.
https://backstage.io/docs/deployment/docker
`),
);
const pkgPath = paths.resolveTarget(PKG_PATH);
const pkg = await fs.readJson(pkgPath);
const appConfigs = await findAppConfigs();
+3 -9
View File
@@ -60,11 +60,7 @@ export function registerCommands(program: CommanderStatic) {
.helpOption(', --backstage-cli-help') // Let docker handle --help
.option('--build', 'Build packages before packing them into the image')
.description(
// TODO: Add example use cases in Backstage documentation.
// For example, if a $NPM_TOKEN needs to be exposed, run `backend:build-image --secret
// id=NPM_TOKEN,src=/NPM_TOKEN.txt`.
'Bundles the package into a docker image. All extra args are forwarded to ' +
'`docker image build`.',
'Bundles the package into a docker image. This command is deprecated and will be removed.',
)
.action(lazy(() => import('./backend/buildImage').then(m => m.default)));
@@ -225,10 +221,8 @@ export function registerCommands(program: CommanderStatic) {
.action(lazy(() => import('./buildWorkspace').then(m => m.default)));
program
.command('create-github-app <github-org>', { hidden: true })
.description(
'Create new GitHub App in your organization. This command is experimental and may change in the future.',
)
.command('create-github-app <github-org>')
.description('Create new GitHub App in your organization.')
.action(lazy(() => import('./create-github-app').then(m => m.default)));
}
+10
View File
@@ -1,5 +1,15 @@
# @backstage/core-api
## 0.2.21
### Patch Changes
- 0160678b1: Made the `RouteRef*` types compatible with the ones exported from `@backstage/core-plugin-api`.
- Updated dependencies [031ccd45f]
- Updated dependencies [e7c5e4b30]
- @backstage/core-plugin-api@0.1.1
- @backstage/theme@0.2.8
## 0.2.20
### Patch Changes
+5 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/core-api",
"description": "Internal Core API used by Backstage plugins and apps",
"version": "0.2.20",
"version": "0.2.21",
"private": false,
"publishConfig": {
"access": "public",
@@ -30,7 +30,8 @@
},
"dependencies": {
"@backstage/config": "^0.1.4",
"@backstage/theme": "^0.2.6",
"@backstage/core-plugin-api": "^0.1.1",
"@backstage/theme": "^0.2.8",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@types/react": "^16.9",
@@ -42,8 +43,8 @@
"zen-observable": "^0.8.15"
},
"devDependencies": {
"@backstage/cli": "^0.6.13",
"@backstage/test-utils": "^0.1.12",
"@backstage/cli": "^0.6.14",
"@backstage/test-utils": "^0.1.13",
"@backstage/test-utils-core": "^0.1.1",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
+6 -1
View File
@@ -16,6 +16,7 @@
import { IconComponent } from '../icons/types';
import { getOrCreateGlobalSingleton } from '../lib/globalObject';
import { RouteRef as NewRouteRef } from '@backstage/core-plugin-api';
export type AnyParams = { [param in string]: string } | undefined;
export type ParamKeys<Params extends AnyParams> = keyof Params extends never
@@ -34,7 +35,11 @@ export type RouteFunc<Params extends AnyParams> = (
...[params]: Params extends undefined ? readonly [] : readonly [Params]
) => string;
export const routeRefType: unique symbol = getOrCreateGlobalSingleton<any>(
type RouteRefType = Exclude<
keyof NewRouteRef,
'params' | 'path' | 'title' | 'icon'
>;
export const routeRefType: RouteRefType = getOrCreateGlobalSingleton<any>(
'route-ref-type',
() => Symbol('route-ref-type'),
);
+12
View File
@@ -0,0 +1,12 @@
# @backstage/core-app-api
## 0.1.1
### Patch Changes
- e7c5e4b30: Update installation instructions in README.
- Updated dependencies [031ccd45f]
- Updated dependencies [e7c5e4b30]
- @backstage/core-plugin-api@0.1.1
- @backstage/core-components@0.1.1
- @backstage/theme@0.2.8
+6 -6
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/core-app-api",
"description": "Core app API used by Backstage apps",
"version": "0.1.0",
"version": "0.1.1",
"private": false,
"publishConfig": {
"access": "public",
@@ -29,10 +29,10 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/core-components": "^0.1.0",
"@backstage/core-components": "^0.1.1",
"@backstage/config": "^0.1.3",
"@backstage/core-plugin-api": "^0.1.0",
"@backstage/theme": "^0.2.3",
"@backstage/core-plugin-api": "^0.1.1",
"@backstage/theme": "^0.2.8",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@types/react": "^16.9",
@@ -44,8 +44,8 @@
"zen-observable": "^0.8.15"
},
"devDependencies": {
"@backstage/cli": "^0.6.3",
"@backstage/test-utils": "^0.1.8",
"@backstage/cli": "^0.6.14",
"@backstage/test-utils": "^0.1.13",
"@backstage/test-utils-core": "^0.1.1",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
+11
View File
@@ -0,0 +1,11 @@
# @backstage/core-components
## 0.1.1
### Patch Changes
- e7c5e4b30: Update installation instructions in README.
- Updated dependencies [031ccd45f]
- Updated dependencies [e7c5e4b30]
- @backstage/core-plugin-api@0.1.1
- @backstage/theme@0.2.8
+6 -6
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/core-components",
"description": "Core components used by Backstage plugins and apps",
"version": "0.1.0",
"version": "0.1.1",
"private": false,
"publishConfig": {
"access": "public",
@@ -30,9 +30,9 @@
},
"dependencies": {
"@backstage/config": "^0.1.5",
"@backstage/core-plugin-api": "^0.1.0",
"@backstage/core-plugin-api": "^0.1.1",
"@backstage/errors": "^0.1.1",
"@backstage/theme": "^0.2.7",
"@backstage/theme": "^0.2.8",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
@@ -70,9 +70,9 @@
"zen-observable": "^0.8.15"
},
"devDependencies": {
"@backstage/core-app-api": "^0.1.0",
"@backstage/cli": "^0.6.11",
"@backstage/test-utils": "^0.1.11",
"@backstage/core-app-api": "^0.1.1",
"@backstage/cli": "^0.6.14",
"@backstage/test-utils": "^0.1.13",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^13.1.8",
+10
View File
@@ -0,0 +1,10 @@
# @backstage/core-plugin-api
## 0.1.1
### Patch Changes
- 031ccd45f: Made the deprecated `icon` fields compatible with the `IconComponent` type from `@backstage/core` in order to smooth out the migration.
- e7c5e4b30: Update installation instructions in README.
- Updated dependencies [e7c5e4b30]
- @backstage/theme@0.2.8
+3 -2
View File
@@ -9,6 +9,7 @@ import { ComponentType } from 'react';
import { Config } from '@backstage/config';
import { default as React_2 } from 'react';
import { ReactNode } from 'react';
import { SvgIconProps } from '@material-ui/core';
// @public
export type AlertApi = {
@@ -206,7 +207,7 @@ export function createRouteRef<Params extends {
id?: string;
params?: ParamKey[];
path?: string;
icon?: IconComponent;
icon?: OldIconComponent;
title?: string;
}): RouteRef<OptionalParams<Params>>;
@@ -424,7 +425,7 @@ export type RouteRef<Params extends AnyParams = any> = {
readonly [routeRefType]: 'absolute';
params: ParamKeys<Params>;
path: string;
icon?: IconComponent;
icon?: OldIconComponent;
title?: string;
};
+5 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/core-plugin-api",
"description": "Core API used by Backstage plugins",
"version": "0.1.0",
"version": "0.1.1",
"private": false,
"publishConfig": {
"access": "public",
@@ -30,7 +30,8 @@
},
"dependencies": {
"@backstage/config": "^0.1.3",
"@backstage/theme": "^0.2.3",
"@backstage/theme": "^0.2.8",
"@material-ui/core": "^4.11.0",
"@types/react": "^16.9",
"history": "^5.0.0",
"prop-types": "^15.7.2",
@@ -40,8 +41,8 @@
"zen-observable": "^0.8.15"
},
"devDependencies": {
"@backstage/cli": "^0.6.12",
"@backstage/test-utils": "^0.1.12",
"@backstage/cli": "^0.6.14",
"@backstage/test-utils": "^0.1.13",
"@backstage/test-utils-core": "^0.1.1",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
+1 -1
View File
@@ -14,4 +14,4 @@
* limitations under the License.
*/
export * from './types';
export type { IconComponent } from './types';
@@ -15,6 +15,7 @@
*/
import { ComponentType } from 'react';
import { SvgIconProps } from '@material-ui/core';
/**
* IconComponent is the common icon type used throughout Backstage when
@@ -31,3 +32,10 @@ import { ComponentType } from 'react';
export type IconComponent = ComponentType<{
fontSize?: 'default' | 'small' | 'large';
}>;
/**
* This exists for backwards compatibility with the old core package.
* It's used in some parts of this package in order to smooth out the
* migration, but it is not exported.
*/
export type OldIconComponent = ComponentType<SvgIconProps>;
@@ -21,13 +21,13 @@ import {
ParamKeys,
OptionalParams,
} from './types';
import { IconComponent } from '../icons/types';
import { OldIconComponent } from '../icons/types';
// TODO(Rugvip): Remove this in the next breaking release, it's exported but unused
export type RouteRefConfig<Params extends AnyParams> = {
params?: ParamKeys<Params>;
path?: string;
icon?: IconComponent;
icon?: OldIconComponent;
title: string;
};
@@ -40,7 +40,7 @@ export class RouteRefImpl<Params extends AnyParams>
readonly params: ParamKeys<Params>,
private readonly config: {
path?: string;
icon?: IconComponent;
icon?: OldIconComponent;
title?: string;
},
) {}
@@ -79,7 +79,7 @@ export function createRouteRef<
/** @deprecated Route refs no longer decide their own path */
path?: string;
/** @deprecated Route refs no longer decide their own icon */
icon?: IconComponent;
icon?: OldIconComponent;
/** @deprecated Route refs no longer decide their own title */
title?: string;
}): RouteRef<OptionalParams<Params>> {
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { IconComponent } from '../icons/types';
import { OldIconComponent } from '../icons/types';
import { getOrCreateGlobalSingleton } from '../lib/globalObject';
export type AnyParams = { [param in string]: string } | undefined;
@@ -48,7 +48,7 @@ export type RouteRef<Params extends AnyParams = any> = {
/** @deprecated paths are no longer accessed directly from RouteRefs, use useRouteRef instead */
path: string;
/** @deprecated icons are no longer accessed via RouteRefs */
icon?: IconComponent;
icon?: OldIconComponent;
/** @deprecated titles are no longer accessed via RouteRefs */
title?: string;
};
+10
View File
@@ -1,5 +1,15 @@
# @backstage/core
## 0.7.12
### Patch Changes
- 1cf1d351f: Export `CheckboxTree` as we have a storybook for it
- Updated dependencies [e7c5e4b30]
- Updated dependencies [0160678b1]
- @backstage/theme@0.2.8
- @backstage/core-api@0.2.21
## 0.7.11
### Patch Changes
+5 -5
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/core",
"description": "Core API used by Backstage plugins and apps",
"version": "0.7.11",
"version": "0.7.12",
"private": false,
"publishConfig": {
"access": "public",
@@ -30,9 +30,9 @@
},
"dependencies": {
"@backstage/config": "^0.1.5",
"@backstage/core-api": "^0.2.20",
"@backstage/core-api": "^0.2.21",
"@backstage/errors": "^0.1.1",
"@backstage/theme": "^0.2.7",
"@backstage/theme": "^0.2.8",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
@@ -71,8 +71,8 @@
"zen-observable": "^0.8.15"
},
"devDependencies": {
"@backstage/cli": "^0.6.13",
"@backstage/test-utils": "^0.1.12",
"@backstage/cli": "^0.6.14",
"@backstage/test-utils": "^0.1.13",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^13.1.8",
+38
View File
@@ -1,5 +1,43 @@
# @backstage/create-app
## 0.3.24
### Patch Changes
- 1ddf551f4: Add CLI output and README how to start app after create-app CLI
- Updated dependencies [7af9cef07]
- Updated dependencies [497f4ce18]
- Updated dependencies [ee4eb5b40]
- Updated dependencies [84160313e]
- Updated dependencies [3772de8ba]
- Updated dependencies [7e7c71417]
- Updated dependencies [f430b6c6f]
- Updated dependencies [2a942cc9e]
- Updated dependencies [e7c5e4b30]
- Updated dependencies [ebe802bc4]
- Updated dependencies [1cf1d351f]
- Updated dependencies [90a505a77]
- Updated dependencies [76f99a1a0]
- Updated dependencies [1157fa307]
- Updated dependencies [6fe1567a7]
- Updated dependencies [e7a5a3474]
- Updated dependencies [2305ab8fc]
- Updated dependencies [054bcd029]
- Updated dependencies [aad98c544]
- Updated dependencies [63a432e9c]
- Updated dependencies [f46a9e82d]
- @backstage/test-utils@0.1.13
- @backstage/plugin-scaffolder@0.9.7
- @backstage/cli@0.6.14
- @backstage/plugin-catalog@0.6.1
- @backstage/theme@0.2.8
- @backstage/catalog-model@0.8.1
- @backstage/core@0.7.12
- @backstage/plugin-tech-radar@0.4.0
- @backstage/plugin-scaffolder-backend@0.11.5
- @backstage/plugin-catalog-backend@0.10.1
- @backstage/plugin-techdocs@0.9.5
## 0.3.23
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/create-app",
"description": "Create app package for Backstage",
"version": "0.3.23",
"version": "0.3.24",
"private": false,
"publishConfig": {
"access": "public"
+17
View File
@@ -1,5 +1,22 @@
# @backstage/dev-utils
## 0.1.17
### Patch Changes
- e7c5e4b30: Update installation instructions in README.
- Updated dependencies [7af9cef07]
- Updated dependencies [e7c5e4b30]
- Updated dependencies [ebe802bc4]
- Updated dependencies [1cf1d351f]
- Updated dependencies [deaba2e13]
- Updated dependencies [8e919a6f8]
- @backstage/test-utils@0.1.13
- @backstage/theme@0.2.8
- @backstage/catalog-model@0.8.1
- @backstage/core@0.7.12
- @backstage/plugin-catalog-react@0.2.1
## 0.1.16
### Patch Changes
+7 -7
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/dev-utils",
"description": "Utilities for developing Backstage plugins.",
"version": "0.1.16",
"version": "0.1.17",
"private": false,
"publishConfig": {
"access": "public",
@@ -29,12 +29,12 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/core": "^0.7.11",
"@backstage/catalog-model": "^0.8.0",
"@backstage/core": "^0.7.12",
"@backstage/catalog-model": "^0.8.1",
"@backstage/integration-react": "^0.1.2",
"@backstage/plugin-catalog-react": "^0.2.0",
"@backstage/test-utils": "^0.1.12",
"@backstage/theme": "^0.2.3",
"@backstage/plugin-catalog-react": "^0.2.1",
"@backstage/test-utils": "^0.1.13",
"@backstage/theme": "^0.2.8",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@testing-library/jest-dom": "^5.10.1",
@@ -48,7 +48,7 @@
"react-router-dom": "6.0.0-beta.0"
},
"devDependencies": {
"@backstage/cli": "^0.6.13",
"@backstage/cli": "^0.6.14",
"@types/jest": "^26.0.7",
"@types/node": "^14.14.32"
},
+6
View File
@@ -1,5 +1,11 @@
# @backstage/integration
## 0.5.5
### Patch Changes
- 49d7ec169: GitHub App ID can be a string too for environment variables otherwise it will fail validation
## 0.5.4
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/integration",
"version": "0.5.4",
"version": "0.5.5",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -37,9 +37,9 @@
"luxon": "^1.25.0"
},
"devDependencies": {
"@backstage/cli": "^0.6.13",
"@backstage/cli": "^0.6.14",
"@backstage/config-loader": "^0.6.3",
"@backstage/test-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.13",
"@types/jest": "^26.0.7",
"@types/luxon": "^1.25.0",
"msw": "^0.21.2"
+4
View File
@@ -10,12 +10,14 @@ import { JsonObject } from '@backstage/config';
export interface DocumentCollator {
// (undocumented)
execute(): Promise<IndexableDocument[]>;
readonly type: string;
}
// @public
export interface DocumentDecorator {
// (undocumented)
execute(documents: IndexableDocument[]): Promise<IndexableDocument[]>;
readonly types?: string[];
}
// @public
@@ -41,6 +43,8 @@ export interface SearchQuery {
export interface SearchResult {
// (undocumented)
document: IndexableDocument;
// (undocumented)
type: string;
}
// @public (undocumented)
+12
View File
@@ -23,6 +23,7 @@ export interface SearchQuery {
}
export interface SearchResult {
type: string;
document: IndexableDocument;
}
@@ -57,6 +58,11 @@ export interface IndexableDocument {
* search.
*/
export interface DocumentCollator {
/**
* The type or name of the document set returned by this collator. Used as an
* index name by Search Engines.
*/
readonly type: string;
execute(): Promise<IndexableDocument[]>;
}
@@ -65,5 +71,11 @@ export interface DocumentCollator {
* additional metadata.
*/
export interface DocumentDecorator {
/**
* An optional array of document/index types on which this decorator should
* be applied. If no types are provided, this decorator will be applied to
* all document/index types.
*/
readonly types?: string[];
execute(documents: IndexableDocument[]): Promise<IndexableDocument[]>;
}
+12
View File
@@ -1,5 +1,17 @@
# @backstage/techdocs-common
## 0.6.4
### Patch Changes
- aad98c544: Fixes multiple XSS and sanitization bypass vulnerabilities in TechDocs.
- 090594755: Support parsing `mkdocs.yml` files that are using custom yaml tags like
`!!python/name:materialx.emoji.twemoji`.
- Updated dependencies [ebe802bc4]
- Updated dependencies [49d7ec169]
- @backstage/catalog-model@0.8.1
- @backstage/integration@0.5.5
## 0.6.3
### Patch Changes
@@ -63,6 +63,9 @@ class GCSFile {
process.nextTick(() => {
if (fs.existsSync(this.localFilePath)) {
if (readable.eventNames().includes('pipe')) {
readable.emit('pipe');
}
readable.emit('data', fs.readFileSync(this.localFilePath));
readable.emit('end');
} else {
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/techdocs-common",
"description": "Common functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli",
"version": "0.6.3",
"version": "0.6.4",
"main": "src/index.ts",
"types": "src/index.ts",
"private": false,
@@ -39,10 +39,10 @@
"@azure/identity": "^1.2.2",
"@azure/storage-blob": "^12.4.0",
"@backstage/backend-common": "^0.8.1",
"@backstage/catalog-model": "^0.8.0",
"@backstage/catalog-model": "^0.8.1",
"@backstage/config": "^0.1.5",
"@backstage/errors": "^0.1.1",
"@backstage/integration": "^0.5.4",
"@backstage/integration": "^0.5.5",
"@google-cloud/storage": "^5.6.0",
"@types/express": "^4.17.6",
"aws-sdk": "^2.840.0",
@@ -60,7 +60,7 @@
"winston": "^3.2.1"
},
"devDependencies": {
"@backstage/cli": "^0.6.13",
"@backstage/cli": "^0.6.14",
"@types/fs-extra": "^9.0.5",
"@types/git-url-parse": "^9.0.0",
"@types/js-yaml": "^4.0.0",
@@ -286,8 +286,12 @@ describe('AwsS3Publish', () => {
mockFs.restore();
mockFs({
[entityRootDir]: {
html: {
'unsafe.html': '<html></html>',
},
img: {
'with spaces.png': 'found it',
'unsafe.svg': '<svg></svg>',
},
'some folder': {
'also with spaces.js': 'found it too',
@@ -318,5 +322,28 @@ describe('AwsS3Publish', () => {
);
expect(jsResponse.text).toEqual('found it too');
});
it('should pass text/plain content-type for html', async () => {
const {
kind,
metadata: { namespace, name },
} = entity;
const htmlResponse = await request(app).get(
`/${namespace}/${kind}/${name}/html/unsafe.html`,
);
expect(htmlResponse.text).toEqual('<html></html>');
expect(htmlResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
const svgResponse = await request(app).get(
`/${namespace}/${kind}/${name}/img/unsafe.svg`,
);
expect(svgResponse.text).toEqual('<svg></svg>');
expect(svgResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
});
});
});
@@ -343,8 +343,12 @@ describe('publishing with valid credentials', () => {
mockFs.restore();
mockFs({
[entityRootDir]: {
html: {
'unsafe.html': '<html></html>',
},
img: {
'with spaces.png': 'found it',
'unsafe.svg': '<svg></svg>',
},
'some folder': {
'also with spaces.js': 'found it too',
@@ -375,5 +379,28 @@ describe('publishing with valid credentials', () => {
);
expect(jsResponse.text).toEqual('found it too');
});
it('should pass text/plain content-type for html', async () => {
const {
kind,
metadata: { namespace, name },
} = entity;
const htmlResponse = await request(app).get(
`/${namespace}/${kind}/${name}/html/unsafe.html`,
);
expect(htmlResponse.text).toEqual('<html></html>');
expect(htmlResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
const svgResponse = await request(app).get(
`/${namespace}/${kind}/${name}/img/unsafe.svg`,
);
expect(svgResponse.text).toEqual('<svg></svg>');
expect(svgResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
});
});
});
@@ -285,8 +285,12 @@ describe('GoogleGCSPublish', () => {
mockFs.restore();
mockFs({
[entityRootDir]: {
html: {
'unsafe.html': '<html></html>',
},
img: {
'with spaces.png': 'found it',
'unsafe.svg': '<svg></svg>',
},
'some folder': {
'also with spaces.js': 'found it too',
@@ -309,11 +313,36 @@ describe('GoogleGCSPublish', () => {
const pngResponse = await request(app).get(
`/${namespace}/${kind}/${name}/img/with%20spaces.png`,
);
expect(pngResponse.text).toEqual('found it');
expect(Buffer.from(pngResponse.body).toString('utf8')).toEqual(
'found it',
);
const jsResponse = await request(app).get(
`/${namespace}/${kind}/${name}/some%20folder/also%20with%20spaces.js`,
);
expect(jsResponse.text).toEqual('found it too');
});
it('should pass text/plain content-type for html', async () => {
const {
kind,
metadata: { namespace, name },
} = entity;
const htmlResponse = await request(app).get(
`/${namespace}/${kind}/${name}/html/unsafe.html`,
);
expect(htmlResponse.text).toEqual('<html></html>');
expect(htmlResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
const svgResponse = await request(app).get(
`/${namespace}/${kind}/${name}/img/unsafe.svg`,
);
expect(svgResponse.text).toEqual('<svg></svg>');
expect(svgResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
});
});
});
@@ -20,12 +20,18 @@ import { getFileTreeRecursively, getHeadersForFileExtension } from './helpers';
describe('getHeadersForFileExtension', () => {
const correctMapOfExtensions = [
['.html', 'text/html; charset=utf-8'],
['.html', 'text/plain; charset=utf-8'],
['.htm', 'text/plain; charset=utf-8'],
['.HTML', 'text/plain; charset=utf-8'],
['.dhtml', 'text/plain; charset=utf-8'],
['.xhtml', 'text/plain; charset=utf-8'],
['.xml', 'text/plain; charset=utf-8'],
['.css', 'text/css; charset=utf-8'],
['.png', 'image/png'],
['.jpg', 'image/jpeg'],
['.jpeg', 'image/jpeg'],
['.svg', 'image/svg+xml'],
['.svg', 'text/plain; charset=utf-8'],
['.SVG', 'text/plain; charset=utf-8'],
['.json', 'application/json; charset=utf-8'],
['.this-in-not-an-extension', 'text/plain; charset=utf-8'],
];
@@ -16,6 +16,22 @@
import mime from 'mime-types';
import recursiveReadDir from 'recursive-readdir';
/**
* Helper to get the expected content-type for a given file extension. Also
* takes XSS mitigation into account.
*/
const getContentTypeForExtension = (ext: string): string => {
const defaultContentType = 'text/plain; charset=utf-8';
// Prevent sanitization bypass by preventing browsers from directly rendering
// the contents of untrusted files.
if (ext.match(/htm|xml|svg/i)) {
return defaultContentType;
}
return mime.contentType(ext) || defaultContentType;
};
export type responseHeadersType = {
'Content-Type': string;
};
@@ -29,9 +45,8 @@ export const getHeadersForFileExtension = (
fileExtension: string,
): responseHeadersType => {
return {
'Content-Type':
mime.contentType(fileExtension) || 'text/plain; charset=utf-8',
} as responseHeadersType;
'Content-Type': getContentTypeForExtension(fileExtension),
};
};
/**
@@ -16,8 +16,11 @@
import {
getVoidLogger,
PluginEndpointDiscovery,
resolvePackagePath,
} from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import express from 'express';
import request from 'supertest';
import mockFs from 'mock-fs';
import * as os from 'os';
import { LocalPublish } from './local';
@@ -35,11 +38,21 @@ const createMockEntity = (annotations = {}) => {
};
};
const testDiscovery: jest.Mocked<PluginEndpointDiscovery> = {
getBaseUrl: jest.fn().mockResolvedValue('http://localhost:7000/api/techdocs'),
getExternalBaseUrl: jest.fn(),
};
const logger = getVoidLogger();
const tmpDir =
os.platform() === 'win32' ? 'C:\\tmp\\generatedDir' : '/tmp/generatedDir';
const resolvedDir = resolvePackagePath(
'@backstage/plugin-techdocs-backend',
'static/docs',
);
describe('local publisher', () => {
it('should publish generated documentation dir', async () => {
mockFs({
@@ -48,13 +61,6 @@ describe('local publisher', () => {
},
});
const testDiscovery: jest.Mocked<PluginEndpointDiscovery> = {
getBaseUrl: jest
.fn()
.mockResolvedValue('http://localhost:7000/api/techdocs'),
getExternalBaseUrl: jest.fn(),
};
const mockConfig = new ConfigReader({});
const publisher = new LocalPublish(mockConfig, logger, testDiscovery);
@@ -66,4 +72,40 @@ describe('local publisher', () => {
mockFs.restore();
});
describe('docsRouter', () => {
const mockConfig = new ConfigReader({});
const publisher = new LocalPublish(mockConfig, logger, testDiscovery);
let app: express.Express;
beforeEach(() => {
app = express().use(publisher.docsRouter());
mockFs.restore();
mockFs({
[resolvedDir]: {
'unsafe.html': '<html></html>',
'unsafe.svg': '<svg></svg>',
},
});
});
afterEach(() => {
mockFs.restore();
});
it('should pass text/plain content-type for unsafe types', async () => {
const htmlResponse = await request(app).get(`/unsafe.html`);
expect(htmlResponse.text).toEqual('<html></html>');
expect(htmlResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
const svgResponse = await request(app).get(`/unsafe.svg`);
expect(svgResponse.text).toEqual('<svg></svg>');
expect(svgResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
});
});
});
@@ -31,6 +31,7 @@ import {
ReadinessResponse,
TechDocsMetadata,
} from './types';
import { getHeadersForFileExtension } from './helpers';
// TODO: Use a more persistent storage than node_modules or /tmp directory.
// Make it configurable with techdocs.publisher.local.publishDirectory
@@ -132,7 +133,16 @@ export class LocalPublish implements PublisherBase {
}
docsRouter(): express.Handler {
return express.static(staticDocsDir);
return express.static(staticDocsDir, {
// Handle content-type header the same as all other publishers.
setHeaders: (res, filePath) => {
const fileExtension = path.extname(filePath);
const headers = getHeadersForFileExtension(fileExtension);
for (const [header, value] of Object.entries(headers)) {
res.setHeader(header, value);
}
},
});
}
async hasDocsBeenGenerated(entity: Entity): Promise<boolean> {
@@ -291,7 +291,11 @@ describe('OpenStackSwiftPublish', () => {
mockFs.restore();
mockFs({
[entityRootDir]: {
html: {
'unsafe.html': '<html></html>',
},
img: {
'unsafe.svg': '<svg></svg>',
'with spaces.png': 'found it',
},
'some folder': {
@@ -323,5 +327,28 @@ describe('OpenStackSwiftPublish', () => {
);
expect(jsResponse.text).toEqual('found it too');
});
it('should pass text/plain content-type for unsafe types', async () => {
const {
kind,
metadata: { namespace, name },
} = entity;
const htmlResponse = await request(app).get(
`/${namespace}/${kind}/${name}/html/unsafe.html`,
);
expect(htmlResponse.text).toEqual('<html></html>');
expect(htmlResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
const svgResponse = await request(app).get(
`/${namespace}/${kind}/${name}/img/unsafe.svg`,
);
expect(svgResponse.text).toEqual('<svg></svg>');
expect(svgResponse.header).toMatchObject({
'content-type': 'text/plain; charset=utf-8',
});
});
});
});
+11
View File
@@ -1,5 +1,16 @@
# @backstage/test-utils
## 0.1.13
### Patch Changes
- 7af9cef07: Fix a bug in `MockStorageApi` where it unhelpfully returned new empty buckets every single time
- e7c5e4b30: Update installation instructions in README.
- Updated dependencies [e7c5e4b30]
- Updated dependencies [0160678b1]
- @backstage/theme@0.2.8
- @backstage/core-api@0.2.21
## 0.1.12
### Patch Changes
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/test-utils",
"description": "Utilities to test Backstage plugins and apps.",
"version": "0.1.12",
"version": "0.1.13",
"private": false,
"publishConfig": {
"access": "public",
@@ -29,9 +29,9 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/core-api": "^0.2.19",
"@backstage/core-api": "^0.2.21",
"@backstage/test-utils-core": "^0.1.1",
"@backstage/theme": "^0.2.3",
"@backstage/theme": "^0.2.8",
"@material-ui/core": "^4.11.0",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
@@ -45,7 +45,7 @@
"zen-observable": "^0.8.15"
},
"devDependencies": {
"@backstage/cli": "^0.6.12",
"@backstage/cli": "^0.6.14",
"@types/jest": "^26.0.7",
"@types/node": "^14.14.32"
},
+6
View File
@@ -1,5 +1,11 @@
# @backstage/theme
## 0.2.8
### Patch Changes
- e7c5e4b30: Update installation instructions in README.
## 0.2.7
### Patch Changes
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/theme",
"description": "material-ui theme for use with Backstage.",
"version": "0.2.7",
"version": "0.2.8",
"private": false,
"publishConfig": {
"access": "public",
@@ -31,7 +31,7 @@
"@material-ui/core": "^4.11.0"
},
"devDependencies": {
"@backstage/cli": "^0.6.10"
"@backstage/cli": "^0.6.14"
},
"files": [
"dist"
+4 -4
View File
@@ -33,7 +33,7 @@
"@backstage/catalog-model": "^0.8.0",
"@backstage/core": "^0.7.11",
"@backstage/plugin-catalog-react": "^0.2.0",
"@backstage/theme": "^0.2.7",
"@backstage/theme": "^0.2.8",
"@material-icons/font": "^1.0.2",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -49,9 +49,9 @@
"swagger-ui-react": "^3.37.2"
},
"devDependencies": {
"@backstage/cli": "^0.6.13",
"@backstage/dev-utils": "^0.1.16",
"@backstage/test-utils": "^0.1.12",
"@backstage/cli": "^0.6.14",
"@backstage/dev-utils": "^0.1.17",
"@backstage/test-utils": "^0.1.13",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^13.1.8",
+4 -4
View File
@@ -24,7 +24,7 @@
"@backstage/core": "^0.7.11",
"@backstage/errors": "^0.1.1",
"@backstage/plugin-catalog-react": "^0.2.0",
"@backstage/theme": "^0.2.7",
"@backstage/theme": "^0.2.8",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
@@ -34,9 +34,9 @@
"react-use": "^17.2.4"
},
"devDependencies": {
"@backstage/cli": "^0.6.13",
"@backstage/dev-utils": "^0.1.16",
"@backstage/test-utils": "^0.1.12",
"@backstage/cli": "^0.6.14",
"@backstage/dev-utils": "^0.1.17",
"@backstage/test-utils": "^0.1.13",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^13.1.8",
+4 -4
View File
@@ -23,7 +23,7 @@
"@backstage/catalog-model": "^0.8.0",
"@backstage/core": "^0.7.11",
"@backstage/plugin-catalog-react": "^0.2.0",
"@backstage/theme": "^0.2.7",
"@backstage/theme": "^0.2.8",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
@@ -37,9 +37,9 @@
"recharts": "^1.8.5"
},
"devDependencies": {
"@backstage/cli": "^0.6.13",
"@backstage/dev-utils": "^0.1.16",
"@backstage/test-utils": "^0.1.12",
"@backstage/cli": "^0.6.14",
"@backstage/dev-utils": "^0.1.17",
"@backstage/test-utils": "^0.1.13",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^13.1.8",
+13
View File
@@ -1,5 +1,18 @@
# @backstage/plugin-catalog-backend
## 0.10.1
### Patch Changes
- e7a5a3474: Only validate the envelope for emitted entities, and defer full validation to when they get processed later on.
- 63a432e9c: Skip deletion of bootstrap location when running the new catalog.
- f46a9e82d: Move dependency to `@microsoft/microsoft-graph-types` from `@backstage/plugin-catalog`
to `@backstage/plugin-catalog-backend`.
- Updated dependencies [ebe802bc4]
- Updated dependencies [49d7ec169]
- @backstage/catalog-model@0.8.1
- @backstage/integration@0.5.5
## 0.10.0
### Minor Changes
@@ -191,11 +191,6 @@ exports.up = async function up(knex) {
table.index(['key'], 'search_key_idx');
table.index(['value'], 'search_value_idx');
});
// Delete bootstrap location which is no longer required.
await knex('locations')
.where({ type: 'bootstrap', target: 'bootstrap' })
.delete();
};
/**
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-catalog-backend",
"version": "0.10.0",
"version": "0.10.1",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -32,10 +32,10 @@
"@azure/msal-node": "^1.0.0-beta.3",
"@backstage/backend-common": "^0.8.1",
"@backstage/catalog-client": "^0.3.12",
"@backstage/catalog-model": "^0.8.0",
"@backstage/catalog-model": "^0.8.1",
"@backstage/config": "^0.1.5",
"@backstage/errors": "^0.1.1",
"@backstage/integration": "^0.5.4",
"@backstage/integration": "^0.5.5",
"@backstage/plugin-search-backend-node": "^0.1.4",
"@backstage/search-common": "^0.1.1",
"@microsoft/microsoft-graph-types": "^1.25.0",
@@ -65,8 +65,8 @@
"yup": "^0.29.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.13",
"@backstage/test-utils": "^0.1.12",
"@backstage/cli": "^0.6.14",
"@backstage/test-utils": "^0.1.13",
"@types/core-js": "^2.5.4",
"@types/git-url-parse": "^9.0.0",
"@types/lodash": "^4.14.151",
@@ -15,12 +15,11 @@
*/
import { getVoidLogger } from '@backstage/backend-common';
import { DefaultProcessingDatabase } from './database/DefaultProcessingDatabase';
import { DefaultCatalogProcessingEngine } from './DefaultCatalogProcessingEngine';
import { Stitcher } from './Stitcher';
import { CatalogProcessingOrchestrator } from './types';
import waitForExpect from 'wait-for-expect';
import { DefaultProcessingDatabase } from './database/DefaultProcessingDatabase';
import { DefaultCatalogProcessingEngine } from './DefaultCatalogProcessingEngine';
import { CatalogProcessingOrchestrator } from './processing/types';
import { Stitcher } from './stitching/Stitcher';
describe('DefaultCatalogProcessingEngine', () => {
const db = ({
@@ -14,20 +14,26 @@
* limitations under the License.
*/
import { stringifyEntityRef } from '@backstage/catalog-model';
import {
Entity,
entityEnvelopeSchemaValidator,
stringifyEntityRef,
} from '@backstage/catalog-model';
import { serializeError } from '@backstage/errors';
import { Logger } from 'winston';
import { ProcessingDatabase } from './database/types';
import { Stitcher } from './Stitcher';
import { CatalogProcessingOrchestrator } from './processing/types';
import { Stitcher } from './stitching/Stitcher';
import {
CatalogProcessingEngine,
CatalogProcessingOrchestrator,
EntityProvider,
EntityProviderConnection,
EntityProviderMutation,
} from './types';
class Connection implements EntityProviderConnection {
readonly validateEntityEnvelope = entityEnvelopeSchemaValidator();
constructor(
private readonly config: {
processingDatabase: ProcessingDatabase;
@@ -37,7 +43,9 @@ class Connection implements EntityProviderConnection {
async applyMutation(mutation: EntityProviderMutation): Promise<void> {
const db = this.config.processingDatabase;
if (mutation.type === 'full') {
this.check(mutation.entities);
await db.transaction(async tx => {
await db.replaceUnprocessedEntities(tx, {
sourceKey: this.config.id,
@@ -47,6 +55,9 @@ class Connection implements EntityProviderConnection {
});
return;
}
this.check(mutation.added);
this.check(mutation.removed);
await db.transaction(async tx => {
await db.replaceUnprocessedEntities(tx, {
sourceKey: this.config.id,
@@ -56,6 +67,16 @@ class Connection implements EntityProviderConnection {
});
});
}
private check(entities: Entity[]) {
for (const entity of entities) {
try {
this.validateEntityEnvelope(entity);
} catch (e) {
throw new TypeError(`Malformed entity envelope, ${e}`);
}
}
}
}
export class DefaultCatalogProcessingEngine implements CatalogProcessingEngine {
@@ -113,14 +134,18 @@ export class DefaultCatalogProcessingEngine implements CatalogProcessingEngine {
// TODO: replace Promise.all with something more sophisticated for parallel processing.
await Promise.all(
items.map(async item => {
const { id, state, unprocessedEntity } = item;
const { id, state, unprocessedEntity, entityRef } = item;
const result = await this.orchestrator.process({
entity: unprocessedEntity,
state,
});
for (const error of result.errors) {
this.logger.warn(error.message);
// TODO(freben): Try to extract the location out of the unprocessed
// entity and add as meta to the log lines
this.logger.warn(error.message, {
entity: entityRef,
});
}
const errorsString = JSON.stringify(
result.errors.map(e => serializeError(e)),
@@ -1,318 +0,0 @@
/*
* Copyright 2021 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 {
Entity,
EntityRelationSpec,
stringifyEntityRef,
LOCATION_ANNOTATION,
LocationSpec,
LocationEntity,
EntityPolicy,
ORIGIN_LOCATION_ANNOTATION,
stringifyLocationReference,
parseLocationReference,
} from '@backstage/catalog-model';
import {
CatalogProcessor,
CatalogProcessorParser,
CatalogProcessorResult,
} from '../ingestion/processors';
import {
CatalogProcessingOrchestrator,
EntityProcessingRequest,
EntityProcessingResult,
} from './types';
import { Logger } from 'winston';
import { InputError } from '@backstage/errors';
import { locationSpecToLocationEntity } from './util';
import path from 'path';
import * as results from '../ingestion/processors/results';
import { ScmIntegrationRegistry } from '@backstage/integration';
function isLocationEntity(entity: Entity): entity is LocationEntity {
return entity.kind === 'Location';
}
function getEntityOriginLocationRef(entity: Entity): string {
const ref = entity.metadata.annotations?.[ORIGIN_LOCATION_ANNOTATION];
if (!ref) {
const entityRef = stringifyEntityRef(entity);
throw new InputError(
`Entity '${entityRef}' does not have an origin location`,
);
}
return ref;
}
function toAbsoluteUrl(
integrations: ScmIntegrationRegistry,
base: LocationSpec,
type: string,
target: string,
): string {
if (base.type !== type) {
return target;
}
try {
if (type === 'file') {
if (target.startsWith('.')) {
return path.join(path.dirname(base.target), target);
}
return target;
} else if (type === 'url') {
return integrations.resolveUrl({ url: target, base: base.target });
}
return target;
} catch (e) {
return target;
}
}
export class DefaultCatalogProcessingOrchestrator
implements CatalogProcessingOrchestrator {
constructor(
private readonly options: {
processors: CatalogProcessor[];
integrations: ScmIntegrationRegistry;
logger: Logger;
parser: CatalogProcessorParser;
policy: EntityPolicy;
},
) {}
async process(
request: EntityProcessingRequest,
): Promise<EntityProcessingResult> {
// TODO: implement dryRun/eager
return this.processSingleEntity(request.entity);
}
private async processSingleEntity(
unprocessedEntity: Entity,
): Promise<EntityProcessingResult> {
// TODO: validate that this doesn't change during processing
const entityRef = stringifyEntityRef(unprocessedEntity);
// TODO: which one do we actually use here? source-location? - maybe probably doesn't exist yet?
const locationRef =
unprocessedEntity.metadata?.annotations?.[LOCATION_ANNOTATION];
if (!locationRef) {
throw new InputError(`Entity '${entityRef}' does not have a location`);
}
const location = parseLocationReference(locationRef);
const originLocation = parseLocationReference(
getEntityOriginLocationRef(unprocessedEntity),
);
const emitter = createEmitter(this.options.logger, unprocessedEntity);
try {
// Pre-process phase, used to populate entities with data that is required during main processing step
let entity = unprocessedEntity;
for (const processor of this.options.processors) {
if (processor.preProcessEntity) {
try {
entity = await processor.preProcessEntity(
entity,
location,
emitter.emit,
originLocation,
);
} catch (e) {
throw new Error(
`Processor ${processor.constructor.name} threw an error while preprocessing entity ${entityRef} at ${locationRef}, ${e}`,
);
}
}
}
// Enforce entity policies making sure that entities conform to a general schema
let policyEnforcedEntity;
try {
policyEnforcedEntity = await this.options.policy.enforce(entity);
} catch (e) {
throw new InputError(
`Policy check failed while analyzing entity ${entityRef} at ${locationRef}, ${e}`,
);
}
if (!policyEnforcedEntity) {
throw new Error(
`Policy unexpectedly returned no data while analyzing entity ${entityRef} at ${locationRef}`,
);
}
entity = policyEnforcedEntity;
// Validate the given entity kind against its schema
let handled = false;
for (const processor of this.options.processors) {
if (processor.validateEntityKind) {
try {
handled = await processor.validateEntityKind(entity);
if (handled) {
break;
}
} catch (e) {
throw new InputError(
`Processor ${processor.constructor.name} threw an error while validating the entity ${entityRef} at ${locationRef}, ${e}`,
);
}
}
}
if (!handled) {
throw new InputError(
`No processor recognized the entity ${entityRef} at ${locationRef}`,
);
}
// Backwards compatible processing of location entities
if (isLocationEntity(entity)) {
const { type = location.type } = entity.spec;
const targets = new Array<string>();
if (entity.spec.target) {
targets.push(entity.spec.target);
}
if (entity.spec.targets) {
targets.push(...entity.spec.targets);
}
for (const maybeRelativeTarget of targets) {
if (type === 'file' && maybeRelativeTarget.endsWith(path.sep)) {
emitter.emit(
results.inputError(
location,
`LocationEntityProcessor cannot handle ${type} type location with target ${location.target} that ends with a path separator`,
),
);
continue;
}
const target = toAbsoluteUrl(
this.options.integrations,
location,
type,
maybeRelativeTarget,
);
for (const processor of this.options.processors) {
if (processor.readLocation) {
try {
const read = await processor.readLocation(
{
type,
target,
presence: 'required',
},
false,
emitter.emit,
this.options.parser,
);
if (read) {
break;
}
} catch (e) {
throw new Error(
`Processor ${processor.constructor.name} threw an error while postprocessing entity ${entityRef} at ${locationRef}, ${e}`,
);
}
}
}
}
}
// Main processing step of the entity
for (const processor of this.options.processors) {
if (processor.postProcessEntity) {
try {
entity = await processor.postProcessEntity(
entity,
location,
emitter.emit,
);
} catch (e) {
throw new Error(
`Processor ${processor.constructor.name} threw an error while postprocessing entity ${entityRef} at ${locationRef}, ${e}`,
);
}
}
}
return {
...emitter.results(),
completedEntity: entity,
state: new Map(),
ok: true,
};
} catch (error) {
this.options.logger.warn(error.message);
return { ok: false, errors: emitter.results().errors.concat(error) };
}
}
}
function createEmitter(logger: Logger, parentEntity: Entity) {
let done = false;
const errors = new Array<Error>();
const relations = new Array<EntityRelationSpec>();
const deferredEntities = new Array<Entity>();
const emit = (i: CatalogProcessorResult) => {
if (done) {
logger.warn(
`Item if type ${i.type} was emitted after processing had completed at ${
new Error().stack
}`,
);
return;
}
if (i.type === 'entity') {
// TODO(freben): Perform the most basic validation here
// (apiVersion, kind, metadata, metadata.name, metadata.namespace, spec)
const originLocation = getEntityOriginLocationRef(parentEntity);
deferredEntities.push({
...i.entity,
metadata: {
...i.entity.metadata,
annotations: {
...i.entity.metadata.annotations,
[ORIGIN_LOCATION_ANNOTATION]: originLocation,
[LOCATION_ANNOTATION]: stringifyLocationReference(i.location),
},
},
});
} else if (i.type === 'location') {
deferredEntities.push(
locationSpecToLocationEntity(i.location, parentEntity),
);
} else if (i.type === 'relation') {
relations.push(i.relation);
} else if (i.type === 'error') {
errors.push(i.error);
}
};
return {
emit,
results() {
done = true;
return {
errors,
relations,
deferredEntities,
};
},
};
}
@@ -15,7 +15,8 @@
*/
import { DefaultLocationService } from './DefaultLocationService';
import { CatalogProcessingOrchestrator, LocationStore } from './types';
import { CatalogProcessingOrchestrator } from './processing/types';
import { LocationStore } from './types';
describe('DefaultLocationServiceTest', () => {
const orchestrator: jest.Mocked<CatalogProcessingOrchestrator> = {
@@ -27,9 +28,12 @@ describe('DefaultLocationServiceTest', () => {
listLocations: jest.fn(),
getLocation: jest.fn(),
};
beforeEach(() => jest.resetAllMocks());
const locationService = new DefaultLocationService(store, orchestrator);
beforeEach(() => {
jest.resetAllMocks();
});
describe('createLocation', () => {
it('should support dry run', async () => {
orchestrator.process.mockResolvedValueOnce({
@@ -136,6 +140,7 @@ describe('DefaultLocationServiceTest', () => {
});
});
});
describe('listLocations', () => {
it('should call locationStore.deleteLocation', async () => {
await locationService.listLocations();
@@ -14,17 +14,14 @@
* limitations under the License.
*/
import {
LocationSpec,
Location,
Entity,
Location,
LocationSpec,
LOCATION_ANNOTATION,
ORIGIN_LOCATION_ANNOTATION,
} from '@backstage/catalog-model';
import {
LocationService,
LocationStore,
CatalogProcessingOrchestrator,
} from './types';
import { CatalogProcessingOrchestrator } from './processing/types';
import { LocationService, LocationStore } from './types';
import { locationSpecToMetadataName } from './util';
export class DefaultLocationService implements LocationService {
@@ -23,7 +23,6 @@ const createLocationStore = async () => {
const connection = { applyMutation: jest.fn() };
const store = new DefaultLocationStore(knex);
await store.connect(connection);
return { store, connection };
};
@@ -14,22 +14,17 @@
* limitations under the License.
*/
import { LocationSpec, Location } from '@backstage/catalog-model';
import {
LocationStore,
EntityProvider,
EntityProviderConnection,
} from './types';
import { v4 as uuid } from 'uuid';
import { locationSpecToLocationEntity } from './util';
import { Location, LocationSpec } from '@backstage/catalog-model';
import { ConflictError, NotFoundError } from '@backstage/errors';
import { Knex } from 'knex';
type DbLocationsRow = {
id: string;
type: string;
target: string;
};
import { v4 as uuid } from 'uuid';
import { DbLocationsRow } from './database/tables';
import {
EntityProvider,
EntityProviderConnection,
LocationStore,
} from './types';
import { locationSpecToLocationEntity } from './util';
export class DefaultLocationStore implements LocationStore, EntityProvider {
private _connection: EntityProviderConnection | undefined;
@@ -67,11 +67,11 @@ import { CatalogProcessingEngine, LocationService } from '../next/types';
import { ConfigLocationEntityProvider } from './ConfigLocationEntityProvider';
import { DefaultProcessingDatabase } from './database/DefaultProcessingDatabase';
import { DefaultCatalogProcessingEngine } from './DefaultCatalogProcessingEngine';
import { DefaultCatalogProcessingOrchestrator } from './DefaultCatalogProcessingOrchestrator';
import { DefaultLocationService } from './DefaultLocationService';
import { DefaultLocationStore } from './DefaultLocationStore';
import { NextEntitiesCatalog } from './NextEntitiesCatalog';
import { Stitcher } from './Stitcher';
import { DefaultCatalogProcessingOrchestrator } from './processing/DefaultCatalogProcessingOrchestrator';
import { Stitcher } from './stitching/Stitcher';
export type CatalogEnvironment = {
logger: Logger;

Some files were not shown because too many files have changed in this diff Show More