@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/test-utils': patch
|
||||
---
|
||||
|
||||
Fix a bug in `MockStorageApi` where it unhelpfully returned new empty buckets every single time
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
A `<CatalogResultListItem />` component is now available for use in custom Search Experiences.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-cost-insights': patch
|
||||
---
|
||||
|
||||
Move `canvas` package to `devDependencies`.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Remove the trailing space from a the aria-label of the Template "CHOOSE" button.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Exclude core packages from package dependency diff.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Don't merge with previous from state on form changes.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Restructure the next catalog types and files a bit
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
invert logic for when to show type column
|
||||
@@ -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.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/catalog-model': patch
|
||||
---
|
||||
|
||||
Remove the explicit connection from `EntityEnvelope` and `Entity`.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/integration': patch
|
||||
---
|
||||
|
||||
GitHub App ID can be a string too for environment variables otherwise it will fail validation
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Updated dependencies
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/core': patch
|
||||
---
|
||||
|
||||
Export `CheckboxTree` as we have a storybook for it
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Sort `EntityTagPicker` entries.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Add a `<EntityPicker>` field to the scaffolder to pick arbitrary entity kinds, like systems.
|
||||
@@ -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.
|
||||
@@ -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`
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Add CLI output and README how to start app after create-app CLI
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Tweak the `EntityListProvider` to do single-cycle updates
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
chore(deps): bump `@spotify/eslint-config-react` from 9.0.0 to 10.0.0
|
||||
@@ -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`.
|
||||
@@ -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`.
|
||||
@@ -210,6 +210,7 @@ rst
|
||||
rsync
|
||||
ruleset
|
||||
sam
|
||||
sanitization
|
||||
scaffolded
|
||||
scaffolder
|
||||
Scaffolder
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`:
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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": {
|
||||
|
||||
@@ -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',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
@@ -14,3 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import '@testing-library/cypress/add-commands';
|
||||
import './commands';
|
||||
|
||||
+11
-11
@@ -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",
|
||||
|
||||
@@ -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 />;
|
||||
@@ -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 }),
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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'),
|
||||
);
|
||||
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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[]>;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @backstage/theme
|
||||
|
||||
## 0.2.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- e7c5e4b30: Update installation instructions in README.
|
||||
|
||||
## 0.2.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user