Merge branch 'master' into techdocs/addon-integration

Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
Eric Peterson
2022-04-12 11:36:59 +02:00
85 changed files with 1759 additions and 540 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-home': patch
---
Export template logos `TemplateBackstageLogo` and `TemplateBackstageLogoIcon` from package.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-techdocs': patch
---
imports from `@backstage/plugin-search-react` instead of `@backstage/plugin-search`
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Bump the `rushstack` api generator libraries to their latest versions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-kafka-backend': patch
---
build(deps-dev): bump `@types/jest-when` from 2.7.2 to 3.5.0
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
build(deps): bump `npm-packlist` from 3.0.0 to 5.0.0
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder': patch
---
Changed input label for owner field in GitlabRepoPicker
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-todo-backend': patch
---
Fix method to get source-location.
+61
View File
@@ -0,0 +1,61 @@
---
'@backstage/plugin-kubernetes-backend': minor
---
**BREAKING** Custom cluster suppliers need to cache their getClusters result
To allow custom `KubernetesClustersSupplier` instances to refresh the list of clusters
the `getClusters` method is now called whenever the list of clusters is needed.
Existing `KubernetesClustersSupplier` implementations will need to ensure that `getClusters`
can be called frequently and should return a cached result from `getClusters` instead.
For example, here's a simple example of a custom supplier in `packages/backend/src/plugins/kubernetes.ts`:
```diff
-import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend';
+import {
+ ClusterDetails,
+ KubernetesBuilder,
+ KubernetesClustersSupplier,
+} from '@backstage/plugin-kubernetes-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
+import { Duration } from 'luxon';
+
+export class CustomClustersSupplier implements KubernetesClustersSupplier {
+ constructor(private clusterDetails: ClusterDetails[] = []) {}
+
+ static create(refreshInterval: Duration) {
+ const clusterSupplier = new CustomClustersSupplier();
+ // setup refresh, e.g. using a copy of https://github.com/backstage/backstage/blob/master/plugins/search-backend-node/src/runPeriodically.ts
+ runPeriodically(
+ () => clusterSupplier.refreshClusters(),
+ refreshInterval.toMillis(),
+ );
+ return clusterSupplier;
+ }
+
+ async refreshClusters(): Promise<void> {
+ this.clusterDetails = []; // fetch from somewhere
+ }
+
+ async getClusters(): Promise<ClusterDetails[]> {
+ return this.clusterDetails;
+ }
+}
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
- const { router } = await KubernetesBuilder.createBuilder({
+ const builder = await KubernetesBuilder.createBuilder({
logger: env.logger,
config: env.config,
- }).build();
+ });
+ builder.setClusterSupplier(
+ CustomClustersSupplier.create(Duration.fromObject({ minutes: 60 })),
+ );
+ const { router } = await builder.build();
```
+13
View File
@@ -0,0 +1,13 @@
---
'@backstage/create-app': patch
---
Removed `@octokit/rest` and `@gitbeaker/node` from backend dependencies as these are unused in the default app.
To apply these changes to your existing app, remove the following lines from the `dependencies` section of `packages/backend/package.json`
```diff
"@backstage/plugin-techdocs-backend": "^1.0.0",
- "@gitbeaker/node": "^34.6.0",
- "@octokit/rest": "^18.5.3",
```
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend': patch
---
Added `spec.profile.displayName` to search index for Group kinds
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-backend': patch
---
Defensively encode URL parameters when fetching ELB keys
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-search-react': minor
---
New search package to hold things the search plugin itself and other frontend plugins (e.g. techdocs, home) depend on.
+11
View File
@@ -0,0 +1,11 @@
---
'@backstage/plugin-search': patch
---
The following exports has been moved to `@backstage/plugin-search-react` and will be removed in the next release. import from `@backstage/plugin-search-react` instead.
- `SearchApi` interface.
- `searchApiRef`
- `SearchContext`
- `SearchContextProvider`
- `useSearch`
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-tech-insights': minor
---
Add checksId option to EntityTechInsightsScorecardContent component
+5 -2
View File
@@ -26,7 +26,7 @@ _If you're using Backstage in your organization, please try to add your company
| [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling |
| [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. |
| [Trendyol](https://trendyol.com) | [Gamze Senturk](https://github.com/gmzsenturk), [Mert Can Bilgic](https://github.com/mertcb) | The Developer Portal has been called `Pandora`. Provides an overview of Trendyol tech ecosystem. TechDocs, Catalog, Custom Plugins and Theme. |
| [Peloton](https://www.onepeloton.com/) | [Jim Haughwout](https://github.com/JimHaughwout) | Creating our first developer portal and tech-docs. Exploring Service Catalog, Tech Insights and Cost Insights as well. |
| [Peloton](https://www.onepeloton.com/) | [Matt Waldron](https://github.com/daftgopher) | Creating our first developer portal and tech-docs. Exploring Service Catalog, Tech Insights and Cost Insights as well. |
| [TELUS](https://telus.com) | [Seb Barre](https://github.com/sbarre) | The Go-to place to find answers about development and delivery at TELUS. |
| [Brex](https://www.brex.com/) | [Vamsi Chitters](https://github.com/vamsikc) | A centralized UI to understand how a service fits in the whole Brex architecture and manage a teams engineering dependencies. |
| [Oriflame](https://www.oriflame.com/) | [Oriflame](https://github.com/oriflame) | Internal developer portal for services, single page apps and packages overview, API documentation, technical guides, tech-radar and more. |
@@ -34,7 +34,7 @@ _If you're using Backstage in your organization, please try to add your company
| [Netflix](https://www.netflix.com/) | [bleathem](https://github.com/bleathem) | Our Backstage implementation will be the front door to a unified experience connecting our internal platform products across important workflows with integrated knowledge and support. |
| [b.well](https://www.icanbwell.com/) | [Jacob Rosales](https://github.com/jrosales) | Foundation for our engineering portal and cloud insights. |
| [PagerDuty](https://www.pagerduty.com/) | [Mark Shaw](https://github.com/markshawtoronto) | Developer portal, initially focused on software templates and tech-docs. |
| [MoonShiner](https://moonshiner.at) | [Fabian Hippmann](https://github.com/FabianHippmann) | Developer portal - helps us keep track of our customer projects, onboard new developers & improve our development process 🌕🚀🧑‍🚀 |
| [MoonShiner](https://moonshiner.at) | [Fabian Hippmann](https://github.com/FabianHippmann) | Developer portal - helps us keep track of our customer projects, onboard new developers & improve our development process 🌕🚀🧑‍🚀 |
| [FundApps](https://www.fundapps.co/) | [Elliot Greenwood](https://github.com/egnwd) | Developer Portal - A place for us to keep track of our projects and documentation for all services and processes |
| [DAZN](https://dazn.com/) | [Lou Bichard](https://twitter.com/loujaybee), [Marco Crivellaro](https://github.com/crivetechie), [Alex Hollerith](mailto:alex.hollerith@dazn.com) | Ingesting all of DAZN's repos for the catalog, migrating our internal platform apps (pull request boards, release information, inner source marketplace etc) to Backstage plugins (where applicable). |
| [HelloFresh](https://www.hellofresh.de/) | [@iammuho](https://github.com/iammuho), [@ElenaForester](https://github.com/ElenaForester), [@diegomarangoni](https://github.com/diegomarangoni) | Our developer portal at HelloFresh - Spread across an organisation of 500+ engineers globally. |
@@ -110,3 +110,6 @@ _If you're using Backstage in your organization, please try to add your company
| [Beez Innovation Labs Pvt. Ltd](https://www.beezlabs.com/) | [Karthikeyan Venkatesan](https://github.com/karthikeyan23) | Developer portal with software catalog, scaffolding, tech docs, templates, and infra. |
| [Agorapulse](https://www.agorapulse.com/) | [@jvdrean](https://github.com/jvdrean) | Developer portal with software catalog, documentation, monitoring, runbooks, tech radar and more. |
| [Wistia](https://wistia.com/) | [@qrush](https://github.com/qrush), [@okize](https://github.com/okize) | Internal Developer Portal, service catalog, tech docs and more |
| [SIX](https://www.six-group.com/) | [@jbadeau](https://github.com/jbadeau), [@tomassatka](https://github.com/tomassatka) | Internal DevOps portal hosting our software and dataset catalog, as well as custom plugins for observability, service virtualization, deployments, incident managment and quality metrics. |
| [Raiffeisen Bank International](https://www.rbinternational.com/) | [Daniel Baumgartner](https://github.com/dabarbi) | From developers for developers: software catalog, techdocs and heavy use of scaffolder to drive reuse on engineering level forward. Part of inner source initiative. Multi national setup coming. |
| [Spread Group](https://www.spreadgroup.com/) | [Luna Stadler](https://github.com/heyLu), [Iván González](https://github.com/ivangonzalezacuna) | Internal Developer Portal, an overview of all running software, architecture documentation and more; replacing and unifying a variety of internal tools. |
@@ -0,0 +1,269 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="521px" height="601px" viewBox="-0.5 -0.5 521 601" content="&lt;mxfile&gt;&lt;diagram id=&quot;M4OCM2KiCGRnt6vHj1W_&quot; name=&quot;Page-1&quot;&gt;zVpNc6M4EP01HOMyAuP4GDvzkard2VTlsJujAMVoAogSENvz61ctJIwQzmQSYuODjRohxOvX3U/CjrfJ9t84LpK/WUxSB83jvePdOggtAld8g+HQGFw0XzaWLaexsh0ND/QXUca5stY0JqXRsWIsrWhhGiOW5ySqDBvmnO3Mbk8sNe9a4C2xDA8RTm3rvzSuEvVc/vxo/07oNlF3DpA6EeLoectZnavbOch7kp/mdIb1UKp/meCY7Tom74vjbThjVXOU7TckBWg1as11X0+cbafNSV695QLUXPCC05roGct5VQcNhXwaAv3njrfeJbQiDwWO4OxOOF/YkipLRcsVh2XF2TPZsJRxYclZLrqt7Umpeb4QXpF9x6Qm+Y2wjFT8ILrsTV4czObu6BttSjpu0Tas2LBtxz1CIg4UKsMIrSxASCyooprqCTsIkD2t/uscPwJws4Vq3e4VjrJxUI2TAJWs5pG6baDCAPMt6T0fzOhVFDlJcUVfTHIPoaIuvWdUzKRFH12b8KN5D9hmUuqqHrbtNN4Et6upcWTkDzbogr9wKJKOgT1O6TYXx5HAkQgCroFgVET1jTqR0TiGMdaclPQXDuV44IECpi4fZrF2FreDPlmdIG2bhdR4RigPkflqPgvQ0jUZ3bQ+6KirlTGobuoB2NNTST7sIe+dEXGMgsfOmZEjAo0dEW/NE+7CIu6jqGCTYK522SjUXQTB0mSZNwp3vaF4GJW5geWhuxL4kxDxnZMd3C+OaUVZ7qAAZ1Db8rCEn6IOUxqJDjf3d473VZxOBbbrkIujLRzRGZkB7/YFExjHoDk4y9rRRcF8BslhFdeEZWFd/r6wjlBDUa+GIruIIn+wio5QRq+nkjSWdtIILpUzljYhc3BIgiEuirTe0rxLHiEXWZ0Cue7AXEOv7CCjOCaSlie1m3sWigV9nWAxzA0GGBaMQDCthTpg3kuAqPzK7VDsh3DVwF6XkLblZRdG0+vB6frnxHP5zogdXfjqmm7U+YvFrHtt8eyfPIVb4aIQF8MKkIjl3zQKv84wn1L4x9GsGtDP1Kztcvvi5WeQzKuLkdle/3ckUSNm4AHqomAlqBorZ4q12TwkTdqEwhTCPJm4nrcVDAZk0PxZl5UdJzohl4MF7HL6qM2iZ9FHri1Oh8uXkJUiU0jgJKgdHC29MLH61W4jnKN+IX8y9Wtlh7xe7Zw/5JG9Tp3KBot22Tg7LGZZQaPUqqvArFXXn1Gr3iu8xq9VQ8S9mPBCtvCazAYLGlNn+QuTZB+UWeNu/q0sJzzoNWlPNIQDS6y6lDgomRAKnSB+fojF6+wnqAQs61gIr28a/TBNZeDPz6kM2un2eL/5neiaQmCcgvqP46L/Bsh77d3OieS9NLcYA/8zsrdneavRcR3d1oRIqXVdu9cog0WrOGWTYXPFCY6qxuUqRlrV9+eD5bBZNDVt6J9zr2hgJ/gmjrXYlmuaN+AYsSyDnWLwSljrlCcWS5zoUeTCqcmGw7luR0LwYf5CBT0yAvF0ac/4pmMWA5tO7fu/0R1jl5dX8Ljg++hFryQEAyVhpDfSonn8P0CTkY7/ufC+/A8=&lt;/diagram&gt;&lt;/mxfile&gt;" style="background-color: rgb(255, 255, 255);">
<defs/>
<g>
<rect x="0" y="0" width="10" height="10" fill="rgb(255, 255, 255)" stroke="none" pointer-events="all"/>
<path d="M 250 160 L 343.63 160" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 348.88 160 L 341.88 163.5 L 343.63 160 L 341.88 156.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 151px; margin-left: 261px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
No
</div>
</div>
</div>
</foreignObject>
<text x="261" y="154" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">
No
</text>
</switch>
</g>
<path d="M 130 210 L 130 243.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 130 248.88 L 126.5 241.88 L 130 243.63 L 133.5 241.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 221px; margin-left: 131px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
Yes
</div>
</div>
</div>
</foreignObject>
<text x="131" y="224" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">
Yes
</text>
</switch>
</g>
<path d="M 130 110 L 250 160 L 130 210 L 10 160 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 238px; height: 1px; padding-top: 160px; margin-left: 11px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Is the new addition public API?
<br/>
i.e. exported from the package
</div>
</div>
</div>
</foreignObject>
<text x="130" y="164" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Is the new addition public API?...
</text>
</switch>
</g>
<path d="M 130 70 L 130 103.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 130 108.88 L 126.5 101.88 L 130 103.63 L 133.5 101.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<rect x="50" y="10" width="160" height="60" rx="9" ry="9" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 158px; height: 1px; padding-top: 40px; margin-left: 51px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
In what plugin package should I put my code?
</div>
</div>
</div>
</foreignObject>
<text x="130" y="44" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
In what plugin package sho...
</text>
</switch>
</g>
<rect x="350" y="130" width="160" height="60" rx="9" ry="9" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 158px; height: 1px; padding-top: 160px; margin-left: 351px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Put it in the package
<br/>
that uses it
</div>
</div>
</div>
</foreignObject>
<text x="430" y="164" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Put it in the package...
</text>
</switch>
</g>
<path d="M 250 300 L 343.63 300" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 348.88 300 L 341.88 303.5 L 343.63 300 L 341.88 296.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 291px; margin-left: 291px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
Only app/backend
</div>
</div>
</div>
</foreignObject>
<text x="291" y="294" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">
Only app/backend
</text>
</switch>
</g>
<path d="M 130 350 L 130 383.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 130 388.88 L 126.5 381.88 L 130 383.63 L 133.5 381.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 130 250 L 250 300 L 130 350 L 10 300 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 238px; height: 1px; padding-top: 300px; margin-left: 11px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Is the export supposed
<br/>
to be used by other plugins or just app/backend packages?
</div>
</div>
</div>
</foreignObject>
<text x="130" y="304" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Is the export supposed...
</text>
</switch>
</g>
<rect x="350" y="270" width="160" height="60" rx="9" ry="9" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 158px; height: 1px; padding-top: 300px; margin-left: 351px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Put it in the frontend or backend plugin package
</div>
</div>
</div>
</foreignObject>
<text x="430" y="304" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Put it in the frontend or...
</text>
</switch>
</g>
<path d="M 250 440 L 343.63 440" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 348.88 440 L 341.88 443.5 L 343.63 440 L 341.88 436.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 431px; margin-left: 261px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
No
</div>
</div>
</div>
</foreignObject>
<text x="261" y="434" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">
No
</text>
</switch>
</g>
<path d="M 130 490 L 130 523.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 130 528.88 L 126.5 521.88 L 130 523.63 L 133.5 521.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 502px; margin-left: 130px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
Yes
</div>
</div>
</div>
</foreignObject>
<text x="130" y="505" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">
Yes
</text>
</switch>
</g>
<path d="M 130 390 L 250 440 L 130 490 L 10 440 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 238px; height: 1px; padding-top: 440px; margin-left: 11px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Should the export be
<br/>
usable by both Node.js and browser packages?
</div>
</div>
</div>
</foreignObject>
<text x="130" y="444" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Should the export be...
</text>
</switch>
</g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 365px; margin-left: 128px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
Yes, used by other plugins
</div>
</div>
</div>
</foreignObject>
<text x="128" y="368" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">
Yes, used by other plugins
</text>
</switch>
</g>
<rect x="350" y="410" width="160" height="60" rx="9" ry="9" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 158px; height: 1px; padding-top: 440px; margin-left: 351px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Put frontend exports in &lt;plugin&gt;-react, and backend exports in &lt;plugin&gt;-node
</div>
</div>
</div>
</foreignObject>
<text x="430" y="444" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Put frontend exports in &lt;p...
</text>
</switch>
</g>
<rect x="30" y="530" width="200" height="60" rx="9" ry="9" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 560px; margin-left: 31px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Add it to &lt;plugin&gt;-common, but be sure to support both Node.js and web environments
</div>
</div>
</div>
</foreignObject>
<text x="130" y="564" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Add it to &lt;plugin&gt;-common, but be...
</text>
</switch>
</g>
<rect x="510" y="590" width="10" height="10" fill="rgb(255, 255, 255)" stroke="none" pointer-events="all"/>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Viewer does not support full SVG 1.1
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 23 KiB

+10
View File
@@ -57,6 +57,10 @@ This is an array used to determine where to retrieve cluster configuration from.
Valid cluster locator methods are:
- [`config`](#config)
- [`gke`](#gke)
- [custom `KubernetesClustersSupplier`](#custom-kubernetesclusterssupplier)
#### `config`
This cluster locator method will read cluster information from your app-config
@@ -261,6 +265,12 @@ Kubernetes plugin.
Defaults to `false`.
#### Custom `KubernetesClustersSupplier`
If the configuration-based cluster locators do not work for your use-case,
it is also possible to implement a
[custom `KubernetesClustersSupplier`](installation.md#custom-cluster-discovery).
### `customResources` (optional)
Configures which [custom resources][3] to look for when returning an entity's
+57
View File
@@ -90,6 +90,63 @@ async function main() {
That's it! The Kubernetes frontend and backend have now been added to your
Backstage app.
### Custom cluster discovery
If either existing
[cluster locators](https://backstage.io/docs/features/kubernetes/configuration#clusterlocatormethods)
don't work for your use-case, it is possible to implement a custom
[KubernetesClustersSupplier](https://backstage.io/docs/reference/plugin-kubernetes-backend.kubernetesclusterssupplier).
Change the following in `packages/backend/src/plugin/kubernetes.ts`:
```diff
-import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend';
+import {
+ ClusterDetails,
+ KubernetesBuilder,
+ KubernetesClustersSupplier,
+} from '@backstage/plugin-kubernetes-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
+import { Duration } from 'luxon';
+
+export class CustomClustersSupplier implements KubernetesClustersSupplier {
+ constructor(private clusterDetails: ClusterDetails[] = []) {}
+
+ static create(refreshInterval: Duration) {
+ const clusterSupplier = new CustomClustersSupplier();
+ // setup refresh, e.g. using a copy of https://github.com/backstage/backstage/blob/master/plugins/search-backend-node/src/runPeriodically.ts
+ runPeriodically(
+ () => clusterSupplier.refreshClusters(),
+ refreshInterval.toMillis(),
+ );
+ return clusterSupplier;
+ }
+
+ async refreshClusters(): Promise<void> {
+ this.clusterDetails = []; // fetch from somewhere
+ }
+
+ async getClusters(): Promise<ClusterDetails[]> {
+ return this.clusterDetails;
+ }
+}
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
- const { router } = await KubernetesBuilder.createBuilder({
+ const builder = await KubernetesBuilder.createBuilder({
logger: env.logger,
config: env.config,
- }).build();
+ });
+ builder.setClusterSupplier(
+ CustomClustersSupplier.create(Duration.fromObject({ minutes: 60 })),
+ );
+ const { router } = await builder.build();
```
## Running Backstage locally
Start the frontend and the backend app by
+3 -2
View File
@@ -16,11 +16,12 @@ catalog.
You will have to add the processors in the catalog initialization code of your
backend. They are not installed by default, therefore you have to add a
dependency to `@backstage/plugin-catalog-backend-module-github` to your backend
package.
dependency on `@backstage/plugin-catalog-backend-module-github` to your backend
package, plus `@backstage/integration` for the basic credentials management:
```bash
# From your Backstage root directory
yarn add --cwd packages/backend @backstage/integration
yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-github
```
+12
View File
@@ -256,6 +256,18 @@ The Backstage CLI is in a category of its own and is depended on by virtually
all other packages. It's not a library in itself though, and must always be a
development dependency only.
### Deciding where you place your code
It can sometimes be difficult to decide where to place your plugin code. For example
should it go directly in the `-backend` plugin package or in the `-node` package?
As a rule of thumb you should try to keep the exposure of your code as low
as possible. If it doesn't need to be public API, it's best to avoid. If you don't
need it to be used by other plugins, then keep it directly in the plugin packages.
Below is a chart to help you decide where to place your code.
![Package decision](../assets/architecture-overview/package-decision.drawio.svg)
## Databases
As we have seen, both the `lighthouse-audit-service` and `catalog-backend`
+4 -4
View File
@@ -52,10 +52,10 @@
"version": "1.1.0-next.2",
"dependencies": {
"@manypkg/get-packages": "^1.1.3",
"@microsoft/api-documenter": "^7.17.0",
"@microsoft/api-extractor": "^7.19.4",
"@microsoft/api-extractor-model": "^7.16.0",
"@microsoft/tsdoc": "^0.13.2"
"@microsoft/api-documenter": "^7.17.5",
"@microsoft/api-extractor": "^7.21.2",
"@microsoft/api-extractor-model": "^7.16.1",
"@microsoft/tsdoc": "^0.14.1"
},
"devDependencies": {
"@changesets/cli": "^2.14.0",
+3
View File
@@ -10,6 +10,7 @@
"@backstage/app-defaults": "^1.0.1-next.1",
"@backstage/catalog-model": "^1.0.1-next.0",
"@backstage/cli": "^0.17.0-next.1",
"@backstage/config": "^1.0.0",
"@backstage/core-app-api": "^1.0.1-next.0",
"@backstage/core-components": "^0.9.3-next.0",
"@backstage/core-plugin-api": "^1.0.0",
@@ -47,9 +48,11 @@
"@backstage/plugin-rollbar": "^0.4.4-next.0",
"@backstage/plugin-scaffolder": "^1.0.1-next.1",
"@backstage/plugin-search": "^0.7.5-next.0",
"@backstage/plugin-search-react": "^0.0.0",
"@backstage/plugin-search-common": "^0.3.3-next.1",
"@backstage/plugin-sentry": "^0.3.42-next.0",
"@backstage/plugin-shortcuts": "^0.2.5-next.0",
"@backstage/plugin-stack-overflow": "^0.1.0-next.0",
"@backstage/plugin-tech-radar": "^0.5.11-next.1",
"@backstage/plugin-techdocs": "^1.0.1-next.1",
"@backstage/plugin-todo": "^0.2.6-next.0",
@@ -14,14 +14,14 @@
* limitations under the License.
*/
import { TemplateBackstageLogo } from './TemplateBackstageLogo';
import { TemplateBackstageLogoIcon } from './TemplateBackstageLogoIcon';
import {
HomePageToolkit,
HomePageCompanyLogo,
HomePageStarredEntities,
} from '../plugin';
import { wrapInTestApp, TestApiProvider} from '@backstage/test-utils';
TemplateBackstageLogo,
TemplateBackstageLogoIcon
} from '@backstage/plugin-home';
import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils';
import { Content, Page, InfoCard } from '@backstage/core-components';
import {
starredEntitiesApiRef,
@@ -32,10 +32,9 @@ import { configApiRef } from '@backstage/core-plugin-api';
import { ConfigReader } from '@backstage/config';
import {
HomePageSearchBar,
SearchContextProvider,
searchApiRef,
searchPlugin,
} from '@backstage/plugin-search';
import { searchApiRef, SearchContextProvider } from '@backstage/plugin-search-react';
import { HomePageStackOverflowQuestions } from '@backstage/plugin-stack-overflow';
import { Grid, makeStyles } from '@material-ui/core';
import React, { ComponentType } from 'react';
@@ -54,10 +53,7 @@ export default {
<>
<TestApiProvider
apis={[
[
starredEntitiesApiRef,
starredEntitiesApi,
],
[starredEntitiesApiRef, starredEntitiesApi],
[searchApiRef, { query: () => Promise.resolve({ results: [] }) }],
[
configApiRef,
+2 -2
View File
@@ -93,7 +93,7 @@
"mini-css-extract-plugin": "^2.4.2",
"minimatch": "5.0.1",
"node-libs-browser": "^2.2.1",
"npm-packlist": "^3.0.0",
"npm-packlist": "^5.0.0",
"ora": "^5.3.0",
"postcss": "^8.1.0",
"process": "^0.11.10",
@@ -153,7 +153,7 @@
"ts-node": "^10.0.0"
},
"peerDependencies": {
"@microsoft/api-extractor": "^7.19.2"
"@microsoft/api-extractor": "^7.21.2"
},
"peerDependenciesMeta": {
"@microsoft/api-extractor": {
@@ -35,8 +35,6 @@
{{/if}}
"@backstage/plugin-search-backend-node": "^{{version '@backstage/plugin-search-backend-node'}}",
"@backstage/plugin-techdocs-backend": "^{{version '@backstage/plugin-techdocs-backend'}}",
"@gitbeaker/node": "^34.6.0",
"@octokit/rest": "^18.5.3",
"dockerode": "^3.3.1",
"express": "^4.17.1",
"express-promise-router": "^4.1.0",
@@ -211,8 +211,10 @@ export class AwsAlbAuthProvider implements AuthProviderRouteHandlers {
if (optionalCacheKey) {
return crypto.createPublicKey(optionalCacheKey);
}
const keyText: string = await fetch(
`https://public-keys.auth.elb.${this.region}.amazonaws.com/${keyId}`,
const keyText = await fetch(
`https://public-keys.auth.elb.${encodeURIComponent(
this.region,
)}.amazonaws.com/${encodeURIComponent(keyId)}`,
).then(response => response.text());
const keyValue = crypto.createPublicKey(keyText);
this.keyCache.set(keyId, keyValue.export({ format: 'pem', type: 'spki' }));
@@ -23,11 +23,7 @@ import {
CatalogClient,
GetEntitiesRequest,
} from '@backstage/catalog-client';
import {
Entity,
stringifyEntityRef,
UserEntity,
} from '@backstage/catalog-model';
import { stringifyEntityRef } from '@backstage/catalog-model';
import { Config } from '@backstage/config';
import { DocumentCollatorFactory } from '@backstage/plugin-search-common';
import {
@@ -36,6 +32,7 @@ import {
} from '@backstage/plugin-catalog-common';
import { Permission } from '@backstage/plugin-permission-common';
import { Readable } from 'stream';
import { getDocumentText } from './util';
/** @public */
export type DefaultCatalogCollatorFactoryOptions = {
@@ -100,24 +97,6 @@ export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory {
return formatted.toLowerCase();
}
private isUserEntity(entity: Entity): entity is UserEntity {
return entity.kind.toLocaleUpperCase('en-US') === 'USER';
}
private getDocumentText(entity: Entity): string {
let documentText = entity.metadata.description || '';
if (this.isUserEntity(entity)) {
if (entity.spec?.profile?.displayName && documentText) {
// combine displayName and description
const displayName = entity.spec?.profile?.displayName;
documentText = displayName.concat(' : ', documentText);
} else {
documentText = entity.spec?.profile?.displayName || documentText;
}
}
return documentText;
}
private async *execute(): AsyncGenerator<CatalogEntityDocument> {
const { token } = await this.tokenManager.getToken();
let entitiesRetrieved = 0;
@@ -150,7 +129,7 @@ export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory {
kind: entity.kind,
name: entity.metadata.name,
}),
text: this.getDocumentText(entity),
text: getDocumentText(entity),
componentType: entity.spec?.type?.toString() || 'other',
type: entity.spec?.type?.toString() || 'other',
namespace: entity.metadata.namespace || 'default',
@@ -0,0 +1,146 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 {
ComponentEntity,
GroupEntity,
UserEntity,
} from '@backstage/catalog-model';
import { getDocumentText } from './util';
describe('getDocumentText', () => {
describe('kind is not User or Group', () => {
test('contains description if set', () => {
const entity = createComponent();
entity.metadata.description = 'The expected description';
const actual = getDocumentText(entity);
expect(actual).toContain(entity.metadata.description);
});
test('is empty if description is not set', () => {
const entity = createComponent();
const actual = getDocumentText(entity);
expect(actual).toEqual('');
});
});
describe('kind is User', () => {
test('contains display name if set', () => {
const entity = createUser();
const actual = getDocumentText(entity);
expect(actual).toContain(entity.spec.profile?.displayName);
});
test('contains description if set', () => {
const entity = createUser();
const actual = getDocumentText(entity);
expect(actual).toContain(entity.metadata.description);
});
test('contains both description and display name if both are set', () => {
const entity = createUser();
const actual = getDocumentText(entity);
expect(actual).toContain(entity.spec.profile?.displayName);
expect(actual).toContain(entity.metadata.description);
});
test('is empty if description and display name are not set', () => {
const entity = createUser();
delete entity.metadata.description;
delete entity.spec.profile?.displayName;
const actual = getDocumentText(entity);
expect(actual).toEqual('');
});
});
describe('kind is Group', () => {
test('contains display name if set', () => {
const entity = createGroup();
const actual = getDocumentText(entity);
expect(actual).toContain(entity.spec.profile?.displayName);
});
test('contains description if set', () => {
const entity = createGroup();
const actual = getDocumentText(entity);
expect(actual).toContain(entity.metadata.description);
});
test('contains both description and display name if both are set', () => {
const entity = createGroup();
const actual = getDocumentText(entity);
expect(actual).toContain(entity.spec.profile?.displayName);
expect(actual).toContain(entity.metadata.description);
});
test('is empty if description and display name are not set', () => {
const entity = createGroup();
delete entity.metadata.description;
delete entity.spec.profile?.displayName;
const actual = getDocumentText(entity);
expect(actual).toEqual('');
});
});
});
function createGroup(): GroupEntity {
return {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
metadata: {
name: 'group-1',
description: 'The expected description',
},
spec: {
type: 'team',
profile: {
displayName: 'Group 1',
},
children: [],
},
};
}
function createUser(): UserEntity {
return {
apiVersion: 'backstage.io/v1alpha1',
kind: 'User',
metadata: {
name: 'user-1',
description: 'The expected description',
},
spec: {
profile: {
displayName: 'User 1',
},
},
};
}
function createComponent(): ComponentEntity {
return {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
name: 'component-1',
},
spec: {
lifecycle: 'experimental',
owner: 'someone',
type: 'service',
},
};
}
@@ -0,0 +1,37 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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, UserEntity } from '@backstage/catalog-model';
function isUserEntity(entity: Entity): entity is UserEntity {
return entity.kind.toLocaleUpperCase('en-US') === 'USER';
}
function isGroupEntity(entity: Entity): entity is UserEntity {
return entity.kind.toLocaleUpperCase('en-US') === 'GROUP';
}
export function getDocumentText(entity: Entity): string {
const documentTexts: string[] = [];
documentTexts.push(entity.metadata.description || '');
if (isUserEntity(entity) || isGroupEntity(entity)) {
if (entity.spec?.profile?.displayName) {
documentTexts.push(entity.spec.profile.displayName);
}
}
return documentTexts.join(' : ');
}
+1 -1
View File
@@ -93,4 +93,4 @@ Additionally, the API is at a very early state, so contributing with additional
### Homepage Templates
We are hoping that we together can build up a collection of Homepage templates. We therefore put together a place where we can collect all the templates for the Home Plugin in the [storybook](https://backstage.io/storybook/?path=/story/plugins-home-templates). If you would like to contribute with a template, start by taking a look at the [DefaultTemplate storybook example to create your own](/plugins/home/src/templates/DefaultTemplate.stories.tsx), and then open a PR with your suggestion.
We are hoping that we together can build up a collection of Homepage templates. We therefore put together a place where we can collect all the templates for the Home Plugin in the [storybook](https://backstage.io/storybook/?path=/story/plugins-home-templates). If you would like to contribute with a template, start by taking a look at the [DefaultTemplate storybook example to create your own](/packages/app/src/components/home/templates/DefaultTemplate.stories.tsx), and then open a PR with your suggestion.
+13
View File
@@ -119,6 +119,19 @@ export const SettingsModal: (props: {
children: JSX.Element;
}) => JSX.Element;
// Warning: (ae-forgotten-export) The symbol "TemplateBackstageLogoProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "TemplateBackstageLogo" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const TemplateBackstageLogo: (
props: TemplateBackstageLogoProps,
) => JSX.Element;
// Warning: (ae-missing-release-tag) "TemplateBackstageLogoIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const TemplateBackstageLogoIcon: () => JSX.Element;
// @public
export const WelcomeTitle: () => JSX.Element;
-1
View File
@@ -38,7 +38,6 @@
"@backstage/core-components": "^0.9.3-next.1",
"@backstage/core-plugin-api": "^1.0.0",
"@backstage/plugin-catalog-react": "^1.0.1-next.2",
"@backstage/plugin-search": "^0.7.5-next.0",
"@backstage/plugin-stack-overflow": "^0.1.0-next.0",
"@backstage/theme": "^0.2.15",
"@backstage/config": "^1.0.0",
@@ -19,9 +19,13 @@ import React from 'react';
type Classes = {
svg: string;
path: string;
}
};
export const TemplateBackstageLogo = (props: { classes: Classes }) => {
type TemplateBackstageLogoProps = {
classes: Classes;
};
export const TemplateBackstageLogo = (props: TemplateBackstageLogoProps) => {
return (
<svg
className={props.classes.svg}
@@ -43,4 +43,3 @@ export const TemplateBackstageLogoIcon = () => {
</svg>
);
};
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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.
*/
export * from './TemplateBackstageLogo';
export * from './TemplateBackstageLogoIcon';
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { TemplateBackstageLogo } from '../../templates';
import { TemplateBackstageLogo } from '../../assets';
import { HomePageCompanyLogo } from '../../plugin';
import { rootRouteRef } from '../../routes';
import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils';
@@ -20,7 +20,7 @@ import { Grid } from '@material-ui/core';
import React, { ComponentType } from 'react';
import { ComponentAccordion } from '../../componentRenderers';
import { HomePageToolkit } from '../../plugin';
import { TemplateBackstageLogoIcon } from '../../templates';
import { TemplateBackstageLogoIcon } from '../../assets';
export default {
title: 'Plugins/Home/Components/Toolkit',
+1
View File
@@ -33,6 +33,7 @@ export {
WelcomeTitle,
} from './plugin';
export { SettingsModal, HeaderWorldClock } from './components';
export * from './assets';
export type { ClockConfig } from './components';
export { createCardExtension } from './extensions';
export type { ComponentRenderer } from './extensions';
+1 -1
View File
@@ -48,7 +48,7 @@
},
"devDependencies": {
"@backstage/cli": "^0.17.0-next.1",
"@types/jest-when": "^2.7.2",
"@types/jest-when": "^3.5.0",
"@types/lodash": "^4.14.151",
"jest-when": "^3.1.0",
"supertest": "^6.1.3"
+10 -7
View File
@@ -4,6 +4,7 @@
```ts
import { Config } from '@backstage/config';
import { Duration } from 'luxon';
import express from 'express';
import type { FetchResponse } from '@backstage/plugin-kubernetes-common';
import type { JsonObject } from '@backstage/types';
@@ -85,18 +86,20 @@ export class KubernetesBuilder {
// (undocumented)
build(): KubernetesBuilderReturn;
// (undocumented)
protected buildClusterSupplier(): KubernetesClustersSupplier;
protected buildClusterSupplier(
refreshInterval: Duration,
): KubernetesClustersSupplier;
// (undocumented)
protected buildCustomResources(): CustomResource[];
// (undocumented)
protected buildFetcher(): KubernetesFetcher;
// (undocumented)
protected buildHttpServiceLocator(
_clusterDetails: ClusterDetails[],
_clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator;
// (undocumented)
protected buildMultiTenantServiceLocator(
clusterDetails: ClusterDetails[],
clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator;
// (undocumented)
protected buildObjectsProvider(
@@ -105,12 +108,12 @@ export class KubernetesBuilder {
// (undocumented)
protected buildRouter(
objectsProvider: KubernetesObjectsProvider,
clusterDetails: ClusterDetails[],
clusterSupplier: KubernetesClustersSupplier,
): express.Router;
// (undocumented)
protected buildServiceLocator(
method: ServiceLocatorMethod,
clusterDetails: ClusterDetails[],
clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator;
// (undocumented)
static createBuilder(env: KubernetesEnvironment): KubernetesBuilder;
@@ -127,6 +130,8 @@ export class KubernetesBuilder {
// (undocumented)
setClusterSupplier(clusterSupplier?: KubernetesClustersSupplier): this;
// (undocumented)
setDefaultClusterRefreshInterval(refreshInterval: Duration): this;
// (undocumented)
setFetcher(fetcher?: KubernetesFetcher): this;
// (undocumented)
setObjectsProvider(objectsProvider?: KubernetesObjectsProvider): this;
@@ -137,7 +142,6 @@ export class KubernetesBuilder {
// @public
export type KubernetesBuilderReturn = Promise<{
router: express.Router;
clusterDetails: ClusterDetails[];
clusterSupplier: KubernetesClustersSupplier;
customResources: CustomResource[];
fetcher: KubernetesFetcher;
@@ -149,7 +153,6 @@ export type KubernetesBuilderReturn = Promise<{
//
// @public (undocumented)
export interface KubernetesClustersSupplier {
// (undocumented)
getClusters(): Promise<ClusterDetails[]>;
}
+4 -2
View File
@@ -43,6 +43,7 @@
"@google-cloud/container": "^3.0.0",
"@kubernetes/client-node": "^0.16.0",
"@types/express": "^4.17.6",
"@types/luxon": "^2.0.4",
"aws-sdk": "^2.840.0",
"aws4": "^1.11.0",
"compression": "^1.7.4",
@@ -52,6 +53,7 @@
"fs-extra": "10.0.1",
"helmet": "^5.0.2",
"lodash": "^4.17.21",
"luxon": "^2.0.2",
"morgan": "^1.10.0",
"stream-buffers": "^3.0.2",
"winston": "^3.2.1",
@@ -60,8 +62,8 @@
"devDependencies": {
"@backstage/cli": "^0.17.0-next.1",
"@types/aws4": "^1.5.1",
"supertest": "^6.1.3",
"aws-sdk-mock": "^5.2.1"
"aws-sdk-mock": "^5.2.1",
"supertest": "^6.1.3"
},
"files": [
"dist",
@@ -17,7 +17,13 @@
import { Config } from '@backstage/config';
import { ForwardedError } from '@backstage/errors';
import * as container from '@google-cloud/container';
import { GKEClusterDetails, KubernetesClustersSupplier } from '../types/types';
import { Duration } from 'luxon';
import { runPeriodically } from '../service/runPeriodically';
import {
ClusterDetails,
GKEClusterDetails,
KubernetesClustersSupplier,
} from '../types/types';
type GkeClusterLocatorOptions = {
projectId: string;
@@ -31,11 +37,14 @@ export class GkeClusterLocator implements KubernetesClustersSupplier {
constructor(
private readonly options: GkeClusterLocatorOptions,
private readonly client: container.v1.ClusterManagerClient,
private clusterDetails: GKEClusterDetails[] | undefined = undefined,
private hasClusterDetails: boolean = false,
) {}
static fromConfigWithClient(
config: Config,
client: container.v1.ClusterManagerClient,
refreshInterval: Duration | undefined = undefined,
): GkeClusterLocator {
const options = {
projectId: config.getString('projectId'),
@@ -45,18 +54,37 @@ export class GkeClusterLocator implements KubernetesClustersSupplier {
config.getOptionalBoolean('skipMetricsLookup') ?? false,
exposeDashboard: config.getOptionalBoolean('exposeDashboard') ?? false,
};
return new GkeClusterLocator(options, client);
const gkeClusterLocator = new GkeClusterLocator(options, client);
if (refreshInterval) {
runPeriodically(
() => gkeClusterLocator.refreshClusters(),
refreshInterval.toMillis(),
);
}
return gkeClusterLocator;
}
static fromConfig(config: Config): GkeClusterLocator {
static fromConfig(
config: Config,
refreshInterval: Duration | undefined = undefined,
): GkeClusterLocator {
return GkeClusterLocator.fromConfigWithClient(
config,
new container.v1.ClusterManagerClient(),
refreshInterval,
);
}
async getClusters(): Promise<ClusterDetails[]> {
if (!this.hasClusterDetails) {
// refresh at least once when first called, when retries are disabled and in tests
await this.refreshClusters();
}
return this.clusterDetails ?? [];
}
// TODO pass caData into the object
async getClusters(): Promise<GKEClusterDetails[]> {
async refreshClusters(): Promise<void> {
const {
projectId,
region,
@@ -70,7 +98,7 @@ export class GkeClusterLocator implements KubernetesClustersSupplier {
try {
const [response] = await this.client.listClusters(request);
return (response.clusters ?? []).map(r => ({
this.clusterDetails = (response.clusters ?? []).map(r => ({
// TODO filter out clusters which don't have name or endpoint
name: r.name ?? 'unknown',
url: `https://${r.endpoint ?? ''}`,
@@ -88,6 +116,7 @@ export class GkeClusterLocator implements KubernetesClustersSupplier {
}
: {}),
}));
this.hasClusterDetails = true;
} catch (e) {
throw new ForwardedError(
`There was an error retrieving clusters from GKE for projectId=${projectId} region=${region}`,
@@ -15,9 +15,9 @@
*/
import { Config, ConfigReader } from '@backstage/config';
import { getCombinedClusterDetails } from './index';
import { getCombinedClusterSupplier } from './index';
describe('getCombinedClusterDetails', () => {
describe('getCombinedClusterSupplier', () => {
it('should retrieve cluster details from config', async () => {
const config: Config = new ConfigReader(
{
@@ -45,7 +45,8 @@ describe('getCombinedClusterDetails', () => {
'ctx',
);
const result = await getCombinedClusterDetails(config);
const clusterSupplier = getCombinedClusterSupplier(config);
const result = await clusterSupplier.getClusters();
expect(result).toStrictEqual([
{
@@ -99,7 +100,7 @@ describe('getCombinedClusterDetails', () => {
'ctx',
);
await expect(getCombinedClusterDetails(config)).rejects.toStrictEqual(
expect(() => getCombinedClusterSupplier(config)).toThrowError(
new Error('Unsupported kubernetes.clusterLocatorMethods: "magic"'),
);
});
@@ -15,38 +15,49 @@
*/
import { Config } from '@backstage/config';
import { ClusterDetails } from '../types/types';
import { Duration } from 'luxon';
import { ClusterDetails, KubernetesClustersSupplier } from '../types/types';
import { ConfigClusterLocator } from './ConfigClusterLocator';
import { GkeClusterLocator } from './GkeClusterLocator';
export const getCombinedClusterDetails = async (
class CombinedClustersSupplier implements KubernetesClustersSupplier {
constructor(readonly clusterSuppliers: KubernetesClustersSupplier[]) {}
async getClusters(): Promise<ClusterDetails[]> {
return await Promise.all(
this.clusterSuppliers.map(supplier => supplier.getClusters()),
)
.then(res => {
return res.flat();
})
.catch(e => {
throw e;
});
}
}
export const getCombinedClusterSupplier = (
rootConfig: Config,
): Promise<ClusterDetails[]> => {
return Promise.all(
rootConfig
.getConfigArray('kubernetes.clusterLocatorMethods')
.map(clusterLocatorMethod => {
const type = clusterLocatorMethod.getString('type');
switch (type) {
case 'config':
return ConfigClusterLocator.fromConfig(
clusterLocatorMethod,
).getClusters();
case 'gke':
return GkeClusterLocator.fromConfig(
clusterLocatorMethod,
).getClusters();
default:
throw new Error(
`Unsupported kubernetes.clusterLocatorMethods: "${type}"`,
);
}
}),
)
.then(res => {
return res.flat();
})
.catch(e => {
throw e;
refreshInterval: Duration | undefined = undefined,
): KubernetesClustersSupplier => {
const clusterSuppliers = rootConfig
.getConfigArray('kubernetes.clusterLocatorMethods')
.map(clusterLocatorMethod => {
const type = clusterLocatorMethod.getString('type');
switch (type) {
case 'config':
return ConfigClusterLocator.fromConfig(clusterLocatorMethod);
case 'gke':
return GkeClusterLocator.fromConfig(
clusterLocatorMethod,
refreshInterval,
);
default:
throw new Error(
`Unsupported kubernetes.clusterLocatorMethods: "${type}"`,
);
}
});
return new CombinedClustersSupplier(clusterSuppliers);
};
@@ -19,7 +19,9 @@ import { MultiTenantServiceLocator } from './MultiTenantServiceLocator';
describe('MultiTenantConfigClusterLocator', () => {
it('empty clusters returns empty cluster details', async () => {
const sut = new MultiTenantServiceLocator([]);
const sut = new MultiTenantServiceLocator({
getClusters: async () => [],
});
const result = await sut.getClustersByServiceId('ignored');
@@ -27,14 +29,18 @@ describe('MultiTenantConfigClusterLocator', () => {
});
it('one clusters returns one cluster details', async () => {
const sut = new MultiTenantServiceLocator([
{
name: 'cluster1',
url: 'http://localhost:8080',
authProvider: 'serviceAccount',
serviceAccountToken: '12345',
const sut = new MultiTenantServiceLocator({
getClusters: async () => {
return [
{
name: 'cluster1',
url: 'http://localhost:8080',
authProvider: 'serviceAccount',
serviceAccountToken: '12345',
},
];
},
]);
});
const result = await sut.getClustersByServiceId('ignored');
@@ -49,19 +55,23 @@ describe('MultiTenantConfigClusterLocator', () => {
});
it('two clusters returns two cluster details', async () => {
const sut = new MultiTenantServiceLocator([
{
name: 'cluster1',
serviceAccountToken: 'token',
url: 'http://localhost:8080',
authProvider: 'serviceAccount',
const sut = new MultiTenantServiceLocator({
getClusters: async () => {
return [
{
name: 'cluster1',
serviceAccountToken: 'token',
url: 'http://localhost:8080',
authProvider: 'serviceAccount',
},
{
name: 'cluster2',
url: 'http://localhost:8081',
authProvider: 'google',
},
];
},
{
name: 'cluster2',
url: 'http://localhost:8081',
authProvider: 'google',
},
]);
});
const result = await sut.getClustersByServiceId('ignored');
@@ -14,20 +14,24 @@
* limitations under the License.
*/
import { ClusterDetails, KubernetesServiceLocator } from '../types/types';
import {
ClusterDetails,
KubernetesClustersSupplier,
KubernetesServiceLocator,
} from '../types/types';
// This locator assumes that every service is located on every cluster
// Therefore it will always return all clusters provided
export class MultiTenantServiceLocator implements KubernetesServiceLocator {
private readonly clusterDetails: ClusterDetails[];
private readonly clusterSupplier: KubernetesClustersSupplier;
constructor(clusterDetails: ClusterDetails[]) {
this.clusterDetails = clusterDetails;
constructor(clusterSupplier: KubernetesClustersSupplier) {
this.clusterSupplier = clusterSupplier;
}
// As this implementation always returns all clusters serviceId is ignored here
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async getClustersByServiceId(_serviceId: string): Promise<ClusterDetails[]> {
return this.clusterDetails;
return this.clusterSupplier.getClusters();
}
}
@@ -17,10 +17,10 @@ import { Config } from '@backstage/config';
import express from 'express';
import Router from 'express-promise-router';
import { Logger } from 'winston';
import { getCombinedClusterDetails } from '../cluster-locator';
import { Duration } from 'luxon';
import { getCombinedClusterSupplier } from '../cluster-locator';
import { MultiTenantServiceLocator } from '../service-locator/MultiTenantServiceLocator';
import {
ClusterDetails,
KubernetesObjectTypes,
ServiceLocatorMethod,
CustomResource,
@@ -50,7 +50,6 @@ export interface KubernetesEnvironment {
*/
export type KubernetesBuilderReturn = Promise<{
router: express.Router;
clusterDetails: ClusterDetails[];
clusterSupplier: KubernetesClustersSupplier;
customResources: CustomResource[];
fetcher: KubernetesFetcher;
@@ -60,6 +59,9 @@ export type KubernetesBuilderReturn = Promise<{
export class KubernetesBuilder {
private clusterSupplier?: KubernetesClustersSupplier;
private defaultClusterRefreshInterval: Duration = Duration.fromObject({
minutes: 60,
});
private objectsProvider?: KubernetesObjectsProvider;
private fetcher?: KubernetesFetcher;
private serviceLocator?: KubernetesServiceLocator;
@@ -91,13 +93,13 @@ export class KubernetesBuilder {
const fetcher = this.fetcher ?? this.buildFetcher();
const clusterSupplier = this.clusterSupplier ?? this.buildClusterSupplier();
const clusterDetails = await this.fetchClusterDetails(clusterSupplier);
const clusterSupplier =
this.clusterSupplier ??
this.buildClusterSupplier(this.defaultClusterRefreshInterval);
const serviceLocator =
this.serviceLocator ??
this.buildServiceLocator(this.getServiceLocatorMethod(), clusterDetails);
this.buildServiceLocator(this.getServiceLocatorMethod(), clusterSupplier);
const objectsProvider =
this.objectsProvider ??
@@ -109,10 +111,9 @@ export class KubernetesBuilder {
objectTypesToFetch: this.getObjectTypesToFetch(),
});
const router = this.buildRouter(objectsProvider, clusterDetails);
const router = this.buildRouter(objectsProvider, clusterSupplier);
return {
clusterDetails,
clusterSupplier,
customResources,
fetcher,
@@ -127,6 +128,11 @@ export class KubernetesBuilder {
return this;
}
public setDefaultClusterRefreshInterval(refreshInterval: Duration) {
this.defaultClusterRefreshInterval = refreshInterval;
return this;
}
public setObjectsProvider(objectsProvider?: KubernetesObjectsProvider) {
this.objectsProvider = objectsProvider;
return this;
@@ -161,13 +167,11 @@ export class KubernetesBuilder {
return customResources;
}
protected buildClusterSupplier(): KubernetesClustersSupplier {
protected buildClusterSupplier(
refreshInterval: Duration,
): KubernetesClustersSupplier {
const config = this.env.config;
return {
getClusters() {
return getCombinedClusterDetails(config);
},
};
return getCombinedClusterSupplier(config, refreshInterval);
}
protected buildObjectsProvider(
@@ -185,13 +189,13 @@ export class KubernetesBuilder {
protected buildServiceLocator(
method: ServiceLocatorMethod,
clusterDetails: ClusterDetails[],
clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator {
switch (method) {
case 'multiTenant':
return this.buildMultiTenantServiceLocator(clusterDetails);
return this.buildMultiTenantServiceLocator(clusterSupplier);
case 'http':
return this.buildHttpServiceLocator(clusterDetails);
return this.buildHttpServiceLocator(clusterSupplier);
default:
throw new Error(
`Unsupported kubernetes.clusterLocatorMethod "${method}"`,
@@ -200,20 +204,20 @@ export class KubernetesBuilder {
}
protected buildMultiTenantServiceLocator(
clusterDetails: ClusterDetails[],
clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator {
return new MultiTenantServiceLocator(clusterDetails);
return new MultiTenantServiceLocator(clusterSupplier);
}
protected buildHttpServiceLocator(
_clusterDetails: ClusterDetails[],
_clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator {
throw new Error('not implemented');
}
protected buildRouter(
objectsProvider: KubernetesObjectsProvider,
clusterDetails: ClusterDetails[],
clusterSupplier: KubernetesClustersSupplier,
): express.Router {
const logger = this.env.logger;
const router = Router();
@@ -236,6 +240,7 @@ export class KubernetesBuilder {
});
router.get('/clusters', async (_, res) => {
const clusterDetails = await this.fetchClusterDetails(clusterSupplier);
res.json({
items: clusterDetails.map(cd => ({
name: cd.name,
@@ -0,0 +1,54 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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.
*/
/**
* Runs a function repeatedly, with a fixed wait between invocations.
*
* Supports async functions, and silently ignores exceptions and rejections.
*
* @param fn - The function to run. May return a Promise.
* @param delayMs - The delay between a completed function invocation and the
* next.
* @returns A function that, when called, stops the invocation loop.
*/
export function runPeriodically(fn: () => any, delayMs: number): () => void {
let cancel: () => void;
let cancelled = false;
const cancellationPromise = new Promise<void>(resolve => {
cancel = () => {
resolve();
cancelled = true;
};
});
const startRefresh = async () => {
while (!cancelled) {
try {
await fn();
} catch {
// ignore intentionally
}
await Promise.race([
new Promise(resolve => setTimeout(resolve, delayMs)),
cancellationPromise,
]);
}
};
startRefresh();
return cancel!;
}
@@ -80,6 +80,12 @@ export type KubernetesObjectTypes =
// Used to load cluster details from different sources
export interface KubernetesClustersSupplier {
/**
* Returns the cached list of clusters.
*
* Implementations _should_ cache the clusters and refresh them periodically,
* as getClusters is called whenever the list of clusters is needed.
*/
getClusters(): Promise<ClusterDetails[]>;
}
@@ -65,7 +65,8 @@ export const GitlabRepoPicker = (props: {
</>
)}
<FormHelperText>
The organization, user or project that this repo will belong to
The organization, groups, subgroups, user, project (also known as
namespaces in gitlab), that this repo will belong to
</FormHelperText>
</FormControl>
<FormControl
+1
View File
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
+3
View File
@@ -0,0 +1,3 @@
# Backstage Search
A search plugin library which holds functionality the [search plugin](/plugins/search/README.md) itself and other frontend plugins (e.g. [techdocs](/plugins/techdocs/README.md), [home](/plugins/home/README.md)) depend on.
+61
View File
@@ -0,0 +1,61 @@
## API Report File for "@backstage/plugin-search-react"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { ApiRef } from '@backstage/core-plugin-api';
import { AsyncState } from 'react-use/lib/useAsync';
import { ComponentProps } from 'react';
import { JsonObject } from '@backstage/types';
import { PropsWithChildren } from 'react';
import { default as React_2 } from 'react';
import { SearchQuery } from '@backstage/plugin-search-common';
import { SearchResultSet } from '@backstage/plugin-search-common';
// @public (undocumented)
export interface SearchApi {
// (undocumented)
query(query: SearchQuery): Promise<SearchResultSet>;
}
// Warning: (ae-forgotten-export) The symbol "QueryResultProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "SearchApiProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export function SearchApiProviderForStorybook(
props: PropsWithChildren<QueryResultProps>,
): JSX.Element;
// @public (undocumented)
export const searchApiRef: ApiRef<SearchApi>;
// @public (undocumented)
export const SearchContextProvider: ({
initialState,
children,
}: React_2.PropsWithChildren<{
initialState?: SearchContextState | undefined;
}>) => JSX.Element;
// Warning: (ae-missing-release-tag) "SearchContextProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export const SearchContextProviderForStorybook: (
props: ComponentProps<typeof SearchContextProvider> & QueryResultProps,
) => JSX.Element;
// @public (undocumented)
export type SearchContextState = {
term: string;
types: string[];
filters: JsonObject;
pageCursor?: string;
};
// Warning: (ae-forgotten-export) The symbol "SearchContextValue" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export const useSearch: () => SearchContextValue;
// (No @packageDocumentation comment for this package)
```
+53
View File
@@ -0,0 +1,53 @@
{
"name": "@backstage/plugin-search-react",
"version": "0.0.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
"types": "dist/index.d.ts"
},
"backstage": {
"role": "web-library"
},
"homepage": "https://backstage.io",
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
"directory": "plugins/search-react"
},
"keywords": [
"backstage"
],
"scripts": {
"build": "backstage-cli package build",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack",
"clean": "backstage-cli package clean",
"start": "backstage-cli package start"
},
"dependencies": {
"@backstage/plugin-search-common": "^0.3.3-next.1",
"@backstage/core-plugin-api": "^1.0.0",
"@backstage/core-app-api": "^1.0.1-next.0",
"react-use": "^17.3.2",
"@backstage/types": "^1.0.0"
},
"peerDependencies": {
"@types/react": "^16.13.1 || ^17.0.0",
"react": "^16.13.1 || ^17.0.0"
},
"devDependencies": {
"@backstage/test-utils": "^1.0.1-next.1",
"@testing-library/react": "^12.1.3",
"@testing-library/react-hooks": "^7.0.2",
"@testing-library/jest-dom": "^5.10.1"
},
"files": [
"dist"
]
}
+32
View File
@@ -0,0 +1,32 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { SearchQuery, SearchResultSet } from '@backstage/plugin-search-common';
import { createApiRef } from '@backstage/core-plugin-api';
/**
* @public
*/
export const searchApiRef = createApiRef<SearchApi>({
id: 'plugin.search.queryservice',
});
/**
* @public
*/
export interface SearchApi {
query(query: SearchQuery): Promise<SearchResultSet>;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2021 The Backstage Authors
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -0,0 +1,147 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { JsonObject } from '@backstage/types';
import { useApi, AnalyticsContext } from '@backstage/core-plugin-api';
import { SearchResultSet } from '@backstage/plugin-search-common';
import React, {
createContext,
PropsWithChildren,
useCallback,
useContext,
useEffect,
useState,
} from 'react';
import useAsync, { AsyncState } from 'react-use/lib/useAsync';
import usePrevious from 'react-use/lib/usePrevious';
import { searchApiRef } from '../api';
type SearchContextValue = {
result: AsyncState<SearchResultSet>;
setTerm: React.Dispatch<React.SetStateAction<string>>;
setTypes: React.Dispatch<React.SetStateAction<string[]>>;
setFilters: React.Dispatch<React.SetStateAction<JsonObject>>;
setPageCursor: React.Dispatch<React.SetStateAction<string | undefined>>;
fetchNextPage?: React.DispatchWithoutAction;
fetchPreviousPage?: React.DispatchWithoutAction;
} & SearchContextState;
/**
*
* @public
*/
export type SearchContextState = {
term: string;
types: string[];
filters: JsonObject;
pageCursor?: string;
};
/**
* @public
*/
export const SearchContext = createContext<SearchContextValue | undefined>(
undefined,
);
/**
* The initial state of `SearchContextProvider`.
*
*/
const searchInitialState: SearchContextState = {
term: '',
pageCursor: undefined,
filters: {},
types: [],
};
/**
* @public
*/
export const SearchContextProvider = ({
initialState = searchInitialState,
children,
}: PropsWithChildren<{ initialState?: SearchContextState }>) => {
const searchApi = useApi(searchApiRef);
const [pageCursor, setPageCursor] = useState<string | undefined>(
initialState.pageCursor,
);
const [filters, setFilters] = useState<JsonObject>(initialState.filters);
const [term, setTerm] = useState<string>(initialState.term);
const [types, setTypes] = useState<string[]>(initialState.types);
const prevTerm = usePrevious(term);
const result = useAsync(
() =>
searchApi.query({
term,
filters,
pageCursor,
types,
}),
[term, filters, types, pageCursor],
);
const hasNextPage =
!result.loading && !result.error && result.value?.nextPageCursor;
const hasPreviousPage =
!result.loading && !result.error && result.value?.previousPageCursor;
const fetchNextPage = useCallback(() => {
setPageCursor(result.value?.nextPageCursor);
}, [result.value?.nextPageCursor]);
const fetchPreviousPage = useCallback(() => {
setPageCursor(result.value?.previousPageCursor);
}, [result.value?.previousPageCursor]);
useEffect(() => {
// Any time a term is reset, we want to start from page 0.
if (term && prevTerm && term !== prevTerm) {
setPageCursor(undefined);
}
}, [term, prevTerm, initialState.pageCursor]);
const value: SearchContextValue = {
result,
filters,
setFilters,
term,
setTerm,
types,
setTypes,
pageCursor,
setPageCursor,
fetchNextPage: hasNextPage ? fetchNextPage : undefined,
fetchPreviousPage: hasPreviousPage ? fetchPreviousPage : undefined,
};
return (
<AnalyticsContext attributes={{ searchTypes: types.sort().join(',') }}>
<SearchContext.Provider value={value} children={children} />
</AnalyticsContext>
);
};
/**
* @public
*/
export const useSearch = () => {
const context = useContext(SearchContext);
if (context === undefined) {
throw new Error('useSearch must be used within a SearchContextProvider');
}
return context;
};
@@ -1,5 +1,5 @@
/*
* Copyright 2021 The Backstage Authors
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ import { ApiProvider } from '@backstage/core-app-api';
import { SearchResultSet } from '@backstage/plugin-search-common';
import { TestApiRegistry } from '@backstage/test-utils';
import React, { ComponentProps, PropsWithChildren } from 'react';
import { searchApiRef } from '../../apis';
import { searchApiRef } from '../api';
import { SearchContextProvider as RealSearchContextProvider } from './SearchContext';
type QueryResultProps = {
@@ -26,7 +26,7 @@ type QueryResultProps = {
/**
* Utility context provider only for use in Storybook stories. You should use
* the real `<SearchContextProvider>` exported by `@backstage/plugin-search` in
* the real `<SearchContextProvider>` exported by `@backstage/plugin-search-react` in
* your app instead of this! In some cases (like the search page) it may
* already be provided on your behalf.
*/
@@ -40,6 +40,10 @@ export const SearchContextProvider = (
);
};
/**
* Utility api provider only for use in Storybook stories.
*
*/
export function SearchApiProvider(props: PropsWithChildren<QueryResultProps>) {
const { mockedResults, children } = props;
const query: any = () => Promise.resolve(mockedResults || {});
@@ -0,0 +1,26 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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.
*/
export {
SearchContextProvider,
SearchContext,
useSearch,
} from './SearchContext';
export type { SearchContextState } from './SearchContext';
export {
SearchContextProvider as SearchContextProviderForStorybook,
SearchApiProvider as SearchApiProviderForStorybook,
} from './SearchContextForStorybook.stories';
+25
View File
@@ -0,0 +1,25 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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.
*/
export { searchApiRef } from './api';
export type { SearchApi } from './api';
export {
SearchContextProvider,
useSearch,
SearchContextProviderForStorybook,
SearchApiProviderForStorybook,
} from './context';
export type { SearchContextState } from './context';
@@ -14,5 +14,4 @@
* limitations under the License.
*/
export { TemplateBackstageLogoIcon } from './TemplateBackstageLogoIcon';
export { TemplateBackstageLogo } from './TemplateBackstageLogo'
import '@testing-library/jest-dom';
+5 -4
View File
@@ -83,7 +83,7 @@ export const Router: () => JSX.Element;
// Warning: (ae-missing-release-tag) "SearchApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public @deprecated (undocumented)
export interface SearchApi {
// (undocumented)
query(query: SearchQuery): Promise<SearchResultSet>;
@@ -91,7 +91,7 @@ export interface SearchApi {
// Warning: (ae-missing-release-tag) "searchApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public @deprecated (undocumented)
export const searchApiRef: ApiRef<SearchApi>;
// @public (undocumented)
@@ -140,7 +140,7 @@ export type SearchBarProps = Partial<SearchBarBaseProps>;
// Warning: (ae-missing-release-tag) "SearchContextProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public @deprecated (undocumented)
export const SearchContextProvider: ({
initialState,
children,
@@ -322,10 +322,11 @@ export type SidebarSearchProps = {
icon?: IconComponent;
};
// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it
// Warning: (ae-forgotten-export) The symbol "SearchContextValue" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "useSearch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public @deprecated (undocumented)
export const useSearch: () => SearchContextValue;
// @public
+1
View File
@@ -39,6 +39,7 @@
"@backstage/core-plugin-api": "^1.0.0",
"@backstage/errors": "^1.0.0",
"@backstage/plugin-catalog-react": "^1.0.1-next.1",
"@backstage/plugin-search-react": "^0.0.0",
"@backstage/plugin-search-common": "^0.3.3-next.1",
"@backstage/theme": "^0.2.15",
"@backstage/types": "^1.0.0",
+7
View File
@@ -21,12 +21,19 @@ import {
} from '@backstage/core-plugin-api';
import { ResponseError } from '@backstage/errors';
import { SearchQuery, SearchResultSet } from '@backstage/plugin-search-common';
import qs from 'qs';
/**
* @deprecated import from `@backstage/plugin-search-react` instead
*/
export const searchApiRef = createApiRef<SearchApi>({
id: 'plugin.search.queryservice',
});
/**
* @deprecated import from `@backstage/plugin-search-react` instead
*/
export interface SearchApi {
query(query: SearchQuery): Promise<SearchResultSet>;
}
@@ -16,7 +16,7 @@
import { Grid, makeStyles, Paper } from '@material-ui/core';
import React, { ComponentType } from 'react';
import { SearchContextProvider } from '../SearchContext/SearchContextForStorybook.stories';
import { SearchContextProviderForStorybook } from '@backstage/plugin-search-react';
import { SearchBar } from './SearchBar';
export default {
@@ -24,13 +24,13 @@ export default {
component: SearchBar,
decorators: [
(Story: ComponentType<{}>) => (
<SearchContextProvider>
<SearchContextProviderForStorybook>
<Grid container direction="row">
<Grid item xs={12}>
<Story />
</Grid>
</Grid>
</SearchContextProvider>
</SearchContextProviderForStorybook>
),
],
};
@@ -51,6 +51,9 @@ export type SearchContextState = {
pageCursor?: string;
};
/**
* @deprecated import from `@backstage/plugin-search-react` instead
*/
export const SearchContext = createContext<SearchContextValue | undefined>(
undefined,
);
@@ -62,6 +65,9 @@ const searchInitialState: SearchContextState = {
types: [],
};
/**
* @deprecated import from `@backstage/plugin-search-react` instead
*/
export const SearchContextProvider = ({
initialState = searchInitialState,
children,
@@ -126,6 +132,9 @@ export const SearchContextProvider = ({
);
};
/**
* @deprecated import from "@backstage/plugin-search-react" instead
*/
export const useSearch = () => {
const context = useContext(SearchContext);
if (context === undefined) {
@@ -16,7 +16,7 @@
import { Grid, Paper } from '@material-ui/core';
import React, { ComponentType } from 'react';
import { SearchContextProvider } from '../SearchContext/SearchContextForStorybook.stories';
import { SearchContextProviderForStorybook } from '@backstage/plugin-search-react';
import { SearchFilter } from './SearchFilter';
export default {
@@ -24,13 +24,13 @@ export default {
component: SearchFilter,
decorators: [
(Story: ComponentType<{}>) => (
<SearchContextProvider>
<SearchContextProviderForStorybook>
<Grid container direction="row">
<Grid item xs={4}>
<Story />
</Grid>
</Grid>
</SearchContextProvider>
</SearchContextProviderForStorybook>
),
],
};
@@ -18,7 +18,7 @@ import { wrapInTestApp } from '@backstage/test-utils';
import { Button } from '@material-ui/core';
import React, { ComponentType } from 'react';
import { rootRouteRef } from '../../plugin';
import { SearchApiProvider } from '../SearchContext/SearchContextForStorybook.stories';
import { SearchApiProviderForStorybook } from '@backstage/plugin-search-react';
import { SearchModal } from './SearchModal';
import { useSearchModal } from './useSearchModal';
@@ -57,9 +57,9 @@ export default {
decorators: [
(Story: ComponentType<{}>) =>
wrapInTestApp(
<SearchApiProvider mockedResults={mockResults}>
<SearchApiProviderForStorybook mockedResults={mockResults}>
<Story />
</SearchApiProvider>,
</SearchApiProviderForStorybook>,
{ mountedRoutes: { '/search': rootRouteRef } },
),
],
@@ -19,7 +19,8 @@ import { List, ListItem } from '@material-ui/core';
import React, { ComponentType } from 'react';
import { MemoryRouter } from 'react-router';
import { DefaultResultListItem } from '../DefaultResultListItem';
import { SearchContextProvider } from '../SearchContext/SearchContextForStorybook.stories';
import { SearchContextProviderForStorybook } from '@backstage/plugin-search-react';
import { SearchResult } from './SearchResult';
const mockResults = {
@@ -57,9 +58,9 @@ export default {
decorators: [
(Story: ComponentType<{}>) => (
<MemoryRouter>
<SearchContextProvider mockedResults={mockResults}>
<SearchContextProviderForStorybook mockedResults={mockResults}>
<Story />
</SearchContextProvider>
</SearchContextProviderForStorybook>
</MemoryRouter>
),
],
@@ -19,7 +19,7 @@ import CatalogIcon from '@material-ui/icons/MenuBook';
import DocsIcon from '@material-ui/icons/Description';
import UsersGroupsIcon from '@material-ui/icons/Person';
import React, { ComponentType } from 'react';
import { SearchContextProvider } from '../SearchContext/SearchContextForStorybook.stories';
import { SearchContextProviderForStorybook } from '@backstage/plugin-search-react';
import { SearchType } from './SearchType';
export default {
@@ -27,13 +27,13 @@ export default {
component: SearchType,
decorators: [
(Story: ComponentType<{}>) => (
<SearchContextProvider>
<SearchContextProviderForStorybook>
<Grid container direction="row">
<Grid item xs={4}>
<Story />
</Grid>
</Grid>
</SearchContextProvider>
</SearchContextProviderForStorybook>
),
],
};
+6
View File
@@ -38,6 +38,10 @@ const serviceEntityPage = (
title="Customized title for the scorecard"
description="Small description about scorecards"
/>
<EntityTechInsightsScorecardContent
title="Show only simpleTestCheck in this card"
checksId={['simpleTestCheck']}
/>
</EntityLayout.Route>
...
</EntityLayoutWrapper>
@@ -46,6 +50,8 @@ const serviceEntityPage = (
It is not obligatory to pass title and description props to `EntityTechInsightsScorecardContent`. If those are left out, default values from `defaultCheckResultRenderers` in `CheckResultRenderer` will be taken, hence `Boolean scorecard` and `This card represents an overview of default boolean Backstage checks`.
You can pass an array `checksId` as a prop with the [Fact Retrievers ids](../tech-insights-backend#creating-fact-retrievers) to limit which checks you want to show in this card, If you don't pass, the default value is show all checks.
## Boolean Scorecard Example
If you follow the [Backend Example](https://github.com/backstage/backstage/tree/master/plugins/tech-insights-backend#backend-example), once the needed facts have been generated the boolean scorecard will look like this:
+3 -1
View File
@@ -34,9 +34,11 @@ export type CheckResultRenderer = {
export const EntityTechInsightsScorecardContent: ({
title,
description,
checksId,
}: {
title?: string | undefined;
description?: string | undefined;
checksId?: string[] | undefined;
}) => JSX.Element;
// @public
@@ -58,7 +60,7 @@ export interface TechInsightsApi {
// (undocumented)
runChecks(
entityParams: CompoundEntityRef,
checks?: Check[],
checks?: string[],
): Promise<CheckResult[]>;
}
@@ -47,7 +47,7 @@ export interface TechInsightsApi {
getAllChecks(): Promise<Check[]>;
runChecks(
entityParams: CompoundEntityRef,
checks?: Check[],
checks?: string[],
): Promise<CheckResult[]>;
runBulkChecks(
entities: CompoundEntityRef[],
@@ -75,13 +75,12 @@ export class TechInsightsClient implements TechInsightsApi {
async runChecks(
entityParams: CompoundEntityRef,
checks?: Check[],
checks?: string[],
): Promise<CheckResult[]> {
const url = await this.discoveryApi.getBaseUrl('tech-insights');
const { token } = await this.identityApi.getCredentials();
const { namespace, kind, name } = entityParams;
const checkIds = checks ? checks.map(check => check.id) : [];
const requestBody = { checks: checkIds.length > 0 ? checkIds : undefined };
const requestBody = { checks };
const response = await fetch(
`${url}/checks/run/${encodeURIComponent(namespace)}/${encodeURIComponent(
kind,
@@ -26,14 +26,16 @@ import { techInsightsApiRef } from '../../api/TechInsightsApi';
export const ScorecardsOverview = ({
title,
description,
checksId,
}: {
title?: string;
description?: string;
checksId?: string[];
}) => {
const api = useApi(techInsightsApiRef);
const { namespace, kind, name } = useParams();
const { value, loading, error } = useAsync(
async () => await api.runChecks({ namespace, kind, name }),
async () => await api.runChecks({ namespace, kind, name }, checksId),
);
if (loading) {
+1 -2
View File
@@ -44,8 +44,7 @@
"@backstage/integration": "^1.1.0-next.1",
"@backstage/integration-react": "^1.0.1-next.1",
"@backstage/plugin-catalog-react": "^1.0.1-next.2",
"@backstage/plugin-catalog": "^1.1.0-next.2",
"@backstage/plugin-search": "^0.7.5-next.0",
"@backstage/plugin-search-react": "^0.0.0",
"@backstage/plugin-techdocs-react": "^0.0.0",
"@backstage/theme": "^0.2.15",
"@backstage/version-bridge": "^1.0.0",
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { ApiProvider } from '@backstage/core-app-api';
import { searchApiRef } from '@backstage/plugin-search';
import { searchApiRef } from '@backstage/plugin-search-react';
import { TestApiRegistry, wrapInTestApp } from '@backstage/test-utils';
import {
act,
@@ -15,7 +15,10 @@
*/
import { CompoundEntityRef } from '@backstage/catalog-model';
import { SearchContextProvider, useSearch } from '@backstage/plugin-search';
import {
SearchContextProvider,
useSearch,
} from '@backstage/plugin-search-react';
import {
makeStyles,
CircularProgress,
+3 -2
View File
@@ -52,8 +52,9 @@ async function main() {
## Scanned Files
The included `TodoReaderService` and `TodoScmReader` works by reading source code of to the entity that is being viewed. The location source code is determined by the value of the [`backstage.io/source-location`
](https://backstage.io/docs/features/software-catalog/well-known-annotations#backstageiosource-location) annotation of the entity, and if that is missing it falls back to the [`backstage.io/managed-by-location `](https://backstage.io/docs/features/software-catalog/well-known-annotations#backstageiomanaged-by-location) annotation. Only `url` locations are currently supported, meaning locally configured `file` locations won't work. Also note that dot-files and folders are ignored.
The included `TodoReaderService` and `TodoScmReader` works by getting the entity source location from the catalog.
The location source code is determined automatically. In case of the source code of the component is not in the same place of the entity YAML file, you can explicitly set the value of the [`backstage.io/source-location`](https://backstage.io/docs/features/software-catalog/well-known-annotations#backstageiosource-location) annotation of the entity, and if that is missing it falls back to the [`backstage.io/managed-by-location `](https://backstage.io/docs/features/software-catalog/well-known-annotations#backstageiomanaged-by-location) annotation. Only `url` locations are currently supported, meaning locally configured `file` locations won't work. Also note that dot-files and folders are ignored.
## Parser Configuration
@@ -310,7 +310,7 @@ describe('TodoReaderService', () => {
});
});
it('should throw if entity does not have a location', async () => {
it('should not throw if entity does not have a location', async () => {
const todoReader = mockTodoReader([]);
const catalogClient = mockCatalogClient({
...mockEntity,
@@ -320,9 +320,8 @@ describe('TodoReaderService', () => {
await expect(service.listTodos({ entity: entityName })).rejects.toEqual(
expect.objectContaining({
name: 'InputError',
message:
'No entity location annotation found for component:default/my-component',
name: 'Error',
message: "Entity 'component:default/my-component' is missing location",
}),
);
});
@@ -334,7 +333,8 @@ describe('TodoReaderService', () => {
metadata: {
...mockEntity.metadata,
annotations: {
['backstage.io/managed-by-location']: 'file:../info.yaml',
['backstage.io/managed-by-location']:
'file:../managed-by-location.yaml',
},
},
});
@@ -343,7 +343,7 @@ describe('TodoReaderService', () => {
await expect(service.listTodos({ entity: entityName })).rejects.toEqual(
expect.objectContaining({
name: 'InputError',
message: `Invalid entity location type for component:default/my-component, got 'file'`,
message: `Invalid entity location type for component:default/my-component, got 'file' for location ../managed-by-location.yaml`,
}),
);
});
@@ -355,7 +355,7 @@ describe('TodoReaderService', () => {
metadata: {
...mockEntity.metadata,
annotations: {
['backstage.io/source-location']: 'file:../info.yaml',
['backstage.io/source-location']: 'file:../source-location.yaml',
},
},
});
@@ -364,7 +364,7 @@ describe('TodoReaderService', () => {
await expect(service.listTodos({ entity: entityName })).rejects.toEqual(
expect.objectContaining({
name: 'InputError',
message: `Invalid entity source location type for component:default/my-component, got 'file'`,
message: `Invalid entity location type for component:default/my-component, got 'file' for location ../source-location.yaml`,
}),
);
});
@@ -17,10 +17,7 @@
import { InputError, NotFoundError } from '@backstage/errors';
import { CatalogApi } from '@backstage/catalog-client';
import {
ANNOTATION_LOCATION,
ANNOTATION_SOURCE_LOCATION,
Entity,
parseLocationRef,
getEntitySourceLocation,
stringifyEntityRef,
} from '@backstage/catalog-model';
import { TodoReader } from '../lib';
@@ -74,8 +71,15 @@ export class TodoReaderService implements TodoService {
`Entity not found, ${stringifyEntityRef(req.entity)}`,
);
}
const url = this.getEntitySourceUrl(entity);
const entitySourceLocation = getEntitySourceLocation(entity);
if (entitySourceLocation.type !== 'url') {
throw new InputError(
`Invalid entity location type for ${stringifyEntityRef(entity)}, got '${
entitySourceLocation.type
}' for location ${entitySourceLocation.target}`,
);
}
const url = entitySourceLocation.target;
const todos = await this.todoReader.readTodos({ url });
let limit = req.limit ?? this.defaultPageSize;
@@ -125,36 +129,4 @@ export class TodoReaderService implements TodoService {
limit,
};
}
private getEntitySourceUrl(entity: Entity) {
const sourceLocation =
entity.metadata.annotations?.[ANNOTATION_SOURCE_LOCATION];
if (sourceLocation) {
const parsed = parseLocationRef(sourceLocation);
if (parsed.type !== 'url') {
throw new InputError(
`Invalid entity source location type for ${stringifyEntityRef(
entity,
)}, got '${parsed.type}'`,
);
}
return parsed.target;
}
const location = entity.metadata.annotations?.[ANNOTATION_LOCATION];
if (location) {
const parsed = parseLocationRef(location);
if (parsed.type !== 'url') {
throw new InputError(
`Invalid entity location type for ${stringifyEntityRef(
entity,
)}, got '${parsed.type}'`,
);
}
return parsed.target;
}
throw new InputError(
`No entity location annotation found for ${stringifyEntityRef(entity)}`,
);
}
}
+1
View File
@@ -6,6 +6,7 @@ const WebpackPluginFailBuildOnWarning = require('./webpack-plugin-fail-build-on-
*/
const BACKSTAGE_CORE_STORIES = [
'packages/core-components',
'packages/app',
'plugins/org',
'plugins/search',
'plugins/home',
+1 -1
View File
@@ -17,7 +17,7 @@
"devDependencies": {
"@storybook/addon-a11y": "^6.4.20",
"@storybook/addon-actions": "^6.4.20",
"@storybook/addon-links": "^6.4.20",
"@storybook/addon-links": "^6.4.21",
"@storybook/addon-storysource": "^6.4.20",
"@storybook/addons": "^6.4.20",
"@storybook/react": "^6.4.20",
+108 -9
View File
@@ -1356,16 +1356,16 @@
util-deprecate "^1.0.2"
uuid-browser "^3.1.0"
"@storybook/addon-links@^6.4.20":
version "6.4.20"
resolved "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-6.4.20.tgz#7e845a20deece65e7e684433d4c66a6ad61da52c"
integrity sha512-TyRuEd/3yRn2N9xasCKuE2bsY0dTRjAquGeg5WEtvHvr8V6QBLYAC4caXwPxIHSTcRQyO5IYYiVzEJ/+219neA==
"@storybook/addon-links@^6.4.21":
version "6.4.21"
resolved "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-6.4.21.tgz#7251406c3060b63684f4de56799385f3675867c6"
integrity sha512-KajbsVAmCLVSKsrPnUEsfWuD5V0lbNBAtdil0EiOqWZU0r3ch92aSMh6H13zfT+lEPlh0PVLKamHur1js1iXGQ==
dependencies:
"@storybook/addons" "6.4.20"
"@storybook/client-logger" "6.4.20"
"@storybook/core-events" "6.4.20"
"@storybook/addons" "6.4.21"
"@storybook/client-logger" "6.4.21"
"@storybook/core-events" "6.4.21"
"@storybook/csf" "0.0.2--canary.87bc651.0"
"@storybook/router" "6.4.20"
"@storybook/router" "6.4.21"
"@types/qs" "^6.9.5"
core-js "^3.8.2"
global "^4.4.0"
@@ -1394,7 +1394,7 @@
react-syntax-highlighter "^13.5.3"
regenerator-runtime "^0.13.7"
"@storybook/addons@6.4.20", "@storybook/addons@^6.4.20":
"@storybook/addons@6.4.20":
version "6.4.20"
resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.4.20.tgz#bbf568b7c4c5a25ef296f285aef0299998ec5933"
integrity sha512-NbsLjDSkE9v2fOr0M7r2hpdYnlYs789ALkXemdTz2y0NUYSPdRfzVVQNXWrgmXivWQRL0aJ3bOjCOc668PPYjg==
@@ -1411,6 +1411,23 @@
global "^4.4.0"
regenerator-runtime "^0.13.7"
"@storybook/addons@6.4.21", "@storybook/addons@^6.4.20":
version "6.4.21"
resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.4.21.tgz#a0081d167eda8a30b2206ccabfe75abae0bb6b58"
integrity sha512-TFLv4FyqP5SBOHEqE6tiW+2++HngkyQ2KRbHICC7khQgRqDkrwvrdKZwzF29igseglhSmftpZrBLXyWbA7q1vg==
dependencies:
"@storybook/api" "6.4.21"
"@storybook/channels" "6.4.21"
"@storybook/client-logger" "6.4.21"
"@storybook/core-events" "6.4.21"
"@storybook/csf" "0.0.2--canary.87bc651.0"
"@storybook/router" "6.4.21"
"@storybook/theming" "6.4.21"
"@types/webpack-env" "^1.16.0"
core-js "^3.8.2"
global "^4.4.0"
regenerator-runtime "^0.13.7"
"@storybook/api@6.4.20":
version "6.4.20"
resolved "https://registry.npmjs.org/@storybook/api/-/api-6.4.20.tgz#65da720985b4b46998a405bddc42c9cef9bad7e4"
@@ -1434,6 +1451,29 @@
ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
"@storybook/api@6.4.21":
version "6.4.21"
resolved "https://registry.npmjs.org/@storybook/api/-/api-6.4.21.tgz#efee41ae7bde37f6fe43ee960fef1a261b1b1dd6"
integrity sha512-AULsLd7ew11IRCpzffyLFGl5cwt9BLMok33DcIlCyvXsiqLm4/OsbgM4sj6QqWVuxcFlWMQJHoRJyeFlULFvZA==
dependencies:
"@storybook/channels" "6.4.21"
"@storybook/client-logger" "6.4.21"
"@storybook/core-events" "6.4.21"
"@storybook/csf" "0.0.2--canary.87bc651.0"
"@storybook/router" "6.4.21"
"@storybook/semver" "^7.3.2"
"@storybook/theming" "6.4.21"
core-js "^3.8.2"
fast-deep-equal "^3.1.3"
global "^4.4.0"
lodash "^4.17.21"
memoizerific "^1.11.3"
regenerator-runtime "^0.13.7"
store2 "^2.12.0"
telejson "^5.3.2"
ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
"@storybook/builder-webpack4@6.4.20":
version "6.4.20"
resolved "https://registry.npmjs.org/@storybook/builder-webpack4/-/builder-webpack4-6.4.20.tgz#e3b5d6b665fbf5a1ec75b7ef32c4c811897ef20d"
@@ -1542,6 +1582,15 @@
ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
"@storybook/channels@6.4.21":
version "6.4.21"
resolved "https://registry.npmjs.org/@storybook/channels/-/channels-6.4.21.tgz#0f1924963f77ec0c3d82aa643a246824ca9f5fca"
integrity sha512-qgy8z3Hp04Q4p+E/8V9MamYYJLW8z1uv1Z+rvosNkg+eAApPg+Qe08BSj59OAUwPLrr2vpBW7WZ/BYSieW1tUg==
dependencies:
core-js "^3.8.2"
ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
"@storybook/client-api@6.4.20":
version "6.4.20"
resolved "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.4.20.tgz#17a24af4bc047f7a6de647b9c1844ab4e40baf83"
@@ -1576,6 +1625,14 @@
core-js "^3.8.2"
global "^4.4.0"
"@storybook/client-logger@6.4.21":
version "6.4.21"
resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.4.21.tgz#7df21cec4d5426669e828af59232ec44ea19c81a"
integrity sha512-XkVCQ5swyYDVh5U+87DGRBdC5utJBpVW7kU5P14TQKMnSc/yHbMcXWaA89K8WKDa/WGkGbc0bKi4WrUwHFg2FA==
dependencies:
core-js "^3.8.2"
global "^4.4.0"
"@storybook/components@6.4.20":
version "6.4.20"
resolved "https://registry.npmjs.org/@storybook/components/-/components-6.4.20.tgz#d063b6a7e70e1be7c8aa79220bb2cd92be8057a1"
@@ -1694,6 +1751,13 @@
dependencies:
core-js "^3.8.2"
"@storybook/core-events@6.4.21":
version "6.4.21"
resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.4.21.tgz#28fff8b10c0d564259edf4439ff8677615ce59c0"
integrity sha512-K6b9M1zYvW/Kfb1cnH6JDfmFvTYDMx/ot9zdl9O5SPH9glUwzOXSk8qKu6GmZTiW2YnC2nKbjaN20mfMsCBPGw==
dependencies:
core-js "^3.8.2"
"@storybook/core-server@6.4.20":
version "6.4.20"
resolved "https://registry.npmjs.org/@storybook/core-server/-/core-server-6.4.20.tgz#6bdf6dd5d83713034df950a98f7638e23c64171c"
@@ -1915,6 +1979,23 @@
react-router-dom "^6.0.0"
ts-dedent "^2.0.0"
"@storybook/router@6.4.21":
version "6.4.21"
resolved "https://registry.npmjs.org/@storybook/router/-/router-6.4.21.tgz#a18172601907918c1442a8a125c9c625d798d09b"
integrity sha512-otn3xYc017SNebeA95xLQ7P6elfyu9541QteXbLR5gFvrT+MB/8zMRZrVuD7n1xwpBgazlonzAdODC736Be9jQ==
dependencies:
"@storybook/client-logger" "6.4.21"
core-js "^3.8.2"
fast-deep-equal "^3.1.3"
global "^4.4.0"
history "5.0.0"
lodash "^4.17.21"
memoizerific "^1.11.3"
qs "^6.10.0"
react-router "^6.0.0"
react-router-dom "^6.0.0"
ts-dedent "^2.0.0"
"@storybook/semver@^7.3.2":
version "7.3.2"
resolved "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz#f3b9c44a1c9a0b933c04e66d0048fcf2fa10dac0"
@@ -1978,6 +2059,24 @@
resolve-from "^5.0.0"
ts-dedent "^2.0.0"
"@storybook/theming@6.4.21":
version "6.4.21"
resolved "https://registry.npmjs.org/@storybook/theming/-/theming-6.4.21.tgz#ea1a33be70c654cb31e5b38fae93f72171e88ef8"
integrity sha512-7pLNwmqbyqCeHXzjsacI69IdJcAZr6zoZA84iGqx+Na32OI8wtIpFczbwuYpVPN2jzgRYp23CgIv1Gz27yk/zw==
dependencies:
"@emotion/core" "^10.1.1"
"@emotion/is-prop-valid" "^0.8.6"
"@emotion/styled" "^10.0.27"
"@storybook/client-logger" "6.4.21"
core-js "^3.8.2"
deep-object-diff "^1.1.0"
emotion-theming "^10.0.27"
global "^4.4.0"
memoizerific "^1.11.3"
polished "^4.0.5"
resolve-from "^5.0.0"
ts-dedent "^2.0.0"
"@storybook/ui@6.4.20":
version "6.4.20"
resolved "https://registry.npmjs.org/@storybook/ui/-/ui-6.4.20.tgz#30e8fba0877b66000841046133d3dc098a807d13"
+248 -296
View File
@@ -1617,16 +1617,16 @@
resolved "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.0.tgz#fe364f025ba74f6de6c837a84ef44bdb1d61e68f"
integrity sha512-mgmE7XBYY/21erpzhexk4Cj1cyTQ9LzvnTxtzM17BJ7ERMNE6W72mQRo0I1Ud8eFJ+RVVIcBNhLFZ3GX4XFz5w==
"@changesets/apply-release-plan@^5.0.5":
version "5.0.5"
resolved "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-5.0.5.tgz#d67b1e022c876d18d887f3c475a3abcad9944b68"
integrity sha512-CxL9dkhzjHiVmXCyHgsLCQj7i/coFTMv/Yy0v6BC5cIWZkQml+lf7zvQqAcFXwY7b54HxRWZPku02XFB53Q0Uw==
"@changesets/apply-release-plan@^6.0.0":
version "6.0.0"
resolved "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-6.0.0.tgz#6c663ff99d919bba3902343d76c35cbbbb046520"
integrity sha512-gp6nIdVdfYdwKww2+f8whckKmvfE4JEm4jJgBhTmooi0uzHWhnxvk6JIzQi89qEAMINN0SeVNnXiAtbFY0Mj3w==
dependencies:
"@babel/runtime" "^7.10.4"
"@changesets/config" "^1.7.0"
"@changesets/config" "^2.0.0"
"@changesets/get-version-range-type" "^0.3.2"
"@changesets/git" "^1.3.1"
"@changesets/types" "^4.1.0"
"@changesets/git" "^1.3.2"
"@changesets/types" "^5.0.0"
"@manypkg/get-packages" "^1.1.3"
detect-indent "^6.0.0"
fs-extra "^7.0.1"
@@ -1636,44 +1636,44 @@
resolve-from "^5.0.0"
semver "^5.4.1"
"@changesets/assemble-release-plan@^5.1.0":
version "5.1.0"
resolved "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-5.1.0.tgz#0fcb18253998e3bc037a554874de43bcc58c4840"
integrity sha512-iYlqffCMhcwZ+6Cv8cimf10OBGYXQKufBI7J6htpRgCV2nT99RKXEjbYOtrXWKQqzu0XxOsk15apSEwjZN0JRw==
"@changesets/assemble-release-plan@^5.1.2":
version "5.1.2"
resolved "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-5.1.2.tgz#63ed3a00f62b5af08a82e83801a252ac9726c625"
integrity sha512-nOFyDw4APSkY/vh5WNwGEtThPgEjVShp03PKVdId6wZTJALVcAALCSLmDRfeqjE2z9EsGJb7hZdDlziKlnqZgw==
dependencies:
"@babel/runtime" "^7.10.4"
"@changesets/errors" "^0.1.4"
"@changesets/get-dependents-graph" "^1.3.1"
"@changesets/types" "^4.1.0"
"@changesets/get-dependents-graph" "^1.3.2"
"@changesets/types" "^5.0.0"
"@manypkg/get-packages" "^1.1.3"
semver "^5.4.1"
"@changesets/changelog-git@^0.1.10":
version "0.1.10"
resolved "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-0.1.10.tgz#df616e92671082a7976381280b4af98ff3a7067d"
integrity sha512-4t7zqPOv3aDZp4Y+AyDhiOG2ypaUXDpOz+MT1wOk3uSZNv78AaDByam0hdk5kfYuH1RlMecWU4/U5lO1ZL5eaA==
"@changesets/changelog-git@^0.1.11":
version "0.1.11"
resolved "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-0.1.11.tgz#80eb45d3562aba2164f25ccc31ac97b9dcd1ded3"
integrity sha512-sWJvAm+raRPeES9usNpZRkooeEB93lOpUN0Lmjz5vhVAb7XGIZrHEJ93155bpE1S0c4oJ5Di9ZWgzIwqhWP/Wg==
dependencies:
"@changesets/types" "^4.1.0"
"@changesets/types" "^5.0.0"
"@changesets/cli@^2.14.0":
version "2.21.0"
resolved "https://registry.npmjs.org/@changesets/cli/-/cli-2.21.0.tgz#b689f91ed908150efc06e0985e6b4cfbd9aea3a3"
integrity sha512-cJXRg28MmF9VbQrlwSjpY4AJA2xZUbXFCpQ3kFmX0IeppO7wknZ2QfocAhIqwM828t8d3R4Zpi5xnvJ/crIcQw==
version "2.22.0"
resolved "https://registry.npmjs.org/@changesets/cli/-/cli-2.22.0.tgz#3bdbfb4a7a81ef37f63114e77da84e23f906b763"
integrity sha512-4bA3YoBkd5cm5WUxmrR2N9WYE7EeQcM+R3bVYMUj2NvffkQVpU3ckAI+z8UICoojq+HRl2OEwtz+S5UBmYY4zw==
dependencies:
"@babel/runtime" "^7.10.4"
"@changesets/apply-release-plan" "^5.0.5"
"@changesets/assemble-release-plan" "^5.1.0"
"@changesets/changelog-git" "^0.1.10"
"@changesets/config" "^1.7.0"
"@changesets/apply-release-plan" "^6.0.0"
"@changesets/assemble-release-plan" "^5.1.2"
"@changesets/changelog-git" "^0.1.11"
"@changesets/config" "^2.0.0"
"@changesets/errors" "^0.1.4"
"@changesets/get-dependents-graph" "^1.3.1"
"@changesets/get-release-plan" "^3.0.6"
"@changesets/git" "^1.3.1"
"@changesets/get-dependents-graph" "^1.3.2"
"@changesets/get-release-plan" "^3.0.8"
"@changesets/git" "^1.3.2"
"@changesets/logger" "^0.0.5"
"@changesets/pre" "^1.0.10"
"@changesets/read" "^0.5.4"
"@changesets/types" "^4.1.0"
"@changesets/write" "^0.1.7"
"@changesets/pre" "^1.0.11"
"@changesets/read" "^0.5.5"
"@changesets/types" "^5.0.0"
"@changesets/write" "^0.1.8"
"@manypkg/get-packages" "^1.1.3"
"@types/is-ci" "^3.0.0"
"@types/semver" "^6.0.0"
@@ -1687,20 +1687,21 @@
outdent "^0.5.0"
p-limit "^2.2.0"
preferred-pm "^3.0.0"
resolve-from "^5.0.0"
semver "^5.4.1"
spawndamnit "^2.0.0"
term-size "^2.1.0"
tty-table "^2.8.10"
"@changesets/config@^1.7.0":
version "1.7.0"
resolved "https://registry.npmjs.org/@changesets/config/-/config-1.7.0.tgz#18353f88ea8153d7f1fb7c321a3fe8667035eddb"
integrity sha512-Ctk6ZO5Ay6oZ95bbKXyA2a1QG0jQUePaGCY6BKkZtUG4PgysesfmiQOPgOY5OsRMt8exJeo6l+DJ75YiKmh0rQ==
"@changesets/config@^2.0.0":
version "2.0.0"
resolved "https://registry.npmjs.org/@changesets/config/-/config-2.0.0.tgz#1770fdfeba2155cf07154c37e96b55cbd27969f0"
integrity sha512-r5bIFY6CN3K6SQ+HZbjyE3HXrBIopONR47mmX7zUbORlybQXtympq9rVAOzc0Oflbap8QeIexc+hikfZoREXDg==
dependencies:
"@changesets/errors" "^0.1.4"
"@changesets/get-dependents-graph" "^1.3.1"
"@changesets/get-dependents-graph" "^1.3.2"
"@changesets/logger" "^0.0.5"
"@changesets/types" "^4.1.0"
"@changesets/types" "^5.0.0"
"@manypkg/get-packages" "^1.1.3"
fs-extra "^7.0.1"
micromatch "^4.0.2"
@@ -1712,28 +1713,28 @@
dependencies:
extendable-error "^0.1.5"
"@changesets/get-dependents-graph@^1.3.1":
version "1.3.1"
resolved "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-1.3.1.tgz#f1ebadbd4e17bb2b987c4542a588e0ee9f2e829a"
integrity sha512-HwUs8U0XK/ZqCQon1/80jJEyswS8JVmTiHTZslrTpuavyhhhxrSpO1eVCdKgaVHBRalOw3gRzdS3uzkmqYsQSQ==
"@changesets/get-dependents-graph@^1.3.2":
version "1.3.2"
resolved "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-1.3.2.tgz#f3ec7ce75f4afb6e3e4b6a87fde065f552c85998"
integrity sha512-tsqA6qZRB86SQuApSoDvI8yEWdyIlo/WLI4NUEdhhxLMJ0dapdeT6rUZRgSZzK1X2nv5YwR0MxQBbDAiDibKrg==
dependencies:
"@changesets/types" "^4.1.0"
"@changesets/types" "^5.0.0"
"@manypkg/get-packages" "^1.1.3"
chalk "^2.1.0"
fs-extra "^7.0.1"
semver "^5.4.1"
"@changesets/get-release-plan@^3.0.6":
version "3.0.6"
resolved "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-3.0.6.tgz#15aac108b9d0f139841562c9372d8cfd738503dc"
integrity sha512-HpPyr8y6xkihy3rONLZ6OtfgYq88NotidPAuS3nwMeZjLHiIVLyejR2+/5q717f6HKcrATxAjTwMAcjl7X/uzA==
"@changesets/get-release-plan@^3.0.8":
version "3.0.8"
resolved "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-3.0.8.tgz#2ac7c4f7903aedede3d27af66311ad1db7937e5d"
integrity sha512-TJYiWNuP0Lzu2dL/KHuk75w7TkiE5HqoYirrXF7SJIxkhlgH9toQf2C7IapiFTObtuF1qDN8HJAX1CuIOwXldg==
dependencies:
"@babel/runtime" "^7.10.4"
"@changesets/assemble-release-plan" "^5.1.0"
"@changesets/config" "^1.7.0"
"@changesets/pre" "^1.0.10"
"@changesets/read" "^0.5.4"
"@changesets/types" "^4.1.0"
"@changesets/assemble-release-plan" "^5.1.2"
"@changesets/config" "^2.0.0"
"@changesets/pre" "^1.0.11"
"@changesets/read" "^0.5.5"
"@changesets/types" "^5.0.0"
"@manypkg/get-packages" "^1.1.3"
"@changesets/get-version-range-type@^0.3.2":
@@ -1741,14 +1742,14 @@
resolved "https://registry.npmjs.org/@changesets/get-version-range-type/-/get-version-range-type-0.3.2.tgz#8131a99035edd11aa7a44c341cbb05e668618c67"
integrity sha512-SVqwYs5pULYjYT4op21F2pVbcrca4qA/bAA3FmFXKMN7Y+HcO8sbZUTx3TAy2VXulP2FACd1aC7f2nTuqSPbqg==
"@changesets/git@^1.3.1":
version "1.3.1"
resolved "https://registry.npmjs.org/@changesets/git/-/git-1.3.1.tgz#e86b4d2b28acdf9bc8949031027a9ac12420b99e"
integrity sha512-yg60QUi38VA0XGXdBy9SRYJhs8xJHE97Z1CaB/hFyByBlh5k1i+avFNBvvw66MsoT/aiml6y9scIG6sC8R5mfg==
"@changesets/git@^1.3.2":
version "1.3.2"
resolved "https://registry.npmjs.org/@changesets/git/-/git-1.3.2.tgz#336051d9a6d965806b1bc473559a9a2cc70773a6"
integrity sha512-p5UL+urAg0Nnpt70DLiBe2iSsMcDubTo9fTOD/61krmcJ466MGh71OHwdAwu1xG5+NKzeysdy1joRTg8CXcEXA==
dependencies:
"@babel/runtime" "^7.10.4"
"@changesets/errors" "^0.1.4"
"@changesets/types" "^4.1.0"
"@changesets/types" "^5.0.0"
"@manypkg/get-packages" "^1.1.3"
is-subdir "^1.1.1"
spawndamnit "^2.0.0"
@@ -1760,51 +1761,56 @@
dependencies:
chalk "^2.1.0"
"@changesets/parse@^0.3.12":
version "0.3.12"
resolved "https://registry.npmjs.org/@changesets/parse/-/parse-0.3.12.tgz#60569bb39ad4ffe47fc01d431613ce5c42e6590f"
integrity sha512-FOBz2L1dT9PcvyQU1Qp2sQ0B4Jw7EgRDAKFVzAQwhzXqCq03TcE7vgKU6VSksCJAioMYDowdVVHNnv/Uak6yZQ==
"@changesets/parse@^0.3.13":
version "0.3.13"
resolved "https://registry.npmjs.org/@changesets/parse/-/parse-0.3.13.tgz#82788c1fc18da4750b07357a7a06142d0d975aa1"
integrity sha512-wh9Ifa0dungY6d2nMz6XxF6FZ/1I7j+mEgPAqrIyKS64nifTh1Ua82qKKMMK05CL7i4wiB2NYc3SfnnCX3RVeA==
dependencies:
"@changesets/types" "^4.1.0"
"@changesets/types" "^5.0.0"
js-yaml "^3.13.1"
"@changesets/pre@^1.0.10":
version "1.0.10"
resolved "https://registry.npmjs.org/@changesets/pre/-/pre-1.0.10.tgz#e677031f271cdab8443b21e0b3cda036a3919c30"
integrity sha512-cZC1C1wTSC17/TcTWivAQ4LAXz5jEYDuy3UeZiBz1wnTTzMHyTHLLwJi60juhl4hawXunDLw0mwZkcpS8Ivitg==
"@changesets/pre@^1.0.11":
version "1.0.11"
resolved "https://registry.npmjs.org/@changesets/pre/-/pre-1.0.11.tgz#46a56790fdceabd03407559bbf91340c8e83fb6a"
integrity sha512-CXZnt4SV9waaC9cPLm7818+SxvLKIDHUxaiTXnJYDp1c56xIexx1BNfC1yMuOdzO2a3rAIcZua5Odxr3dwSKfg==
dependencies:
"@babel/runtime" "^7.10.4"
"@changesets/errors" "^0.1.4"
"@changesets/types" "^4.1.0"
"@changesets/types" "^5.0.0"
"@manypkg/get-packages" "^1.1.3"
fs-extra "^7.0.1"
"@changesets/read@^0.5.4":
version "0.5.4"
resolved "https://registry.npmjs.org/@changesets/read/-/read-0.5.4.tgz#c6dc6ab00c4f70f2ce6766433d92eebde1b00e7a"
integrity sha512-12dTx+p5ztFs9QgJDGHRHR6HzTIbHct9S4lK2I/i6Qkz1cNfAPVIbdoMCdbPIWeLank9muMUjiiFmCWJD7tQIg==
"@changesets/read@^0.5.5":
version "0.5.5"
resolved "https://registry.npmjs.org/@changesets/read/-/read-0.5.5.tgz#9ed90ef3e9f1ba3436ba5580201854a3f4163058"
integrity sha512-bzonrPWc29Tsjvgh+8CqJ0apQOwWim0zheeD4ZK44ApSa/GudnZJTODtA3yNOOuQzeZmL0NUebVoHIurtIkA7w==
dependencies:
"@babel/runtime" "^7.10.4"
"@changesets/git" "^1.3.1"
"@changesets/git" "^1.3.2"
"@changesets/logger" "^0.0.5"
"@changesets/parse" "^0.3.12"
"@changesets/types" "^4.1.0"
"@changesets/parse" "^0.3.13"
"@changesets/types" "^5.0.0"
chalk "^2.1.0"
fs-extra "^7.0.1"
p-filter "^2.1.0"
"@changesets/types@^4.0.1", "@changesets/types@^4.1.0":
"@changesets/types@^4.0.1":
version "4.1.0"
resolved "https://registry.npmjs.org/@changesets/types/-/types-4.1.0.tgz#fb8f7ca2324fd54954824e864f9a61a82cb78fe0"
integrity sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==
"@changesets/write@^0.1.7":
version "0.1.7"
resolved "https://registry.npmjs.org/@changesets/write/-/write-0.1.7.tgz#671def0d871cf5970c5b2f766f0ac4b19ecf1ddb"
integrity sha512-6r+tc6u2l5BBIwEAh7ivRYWFir+XKiw0q/6Hx6NJA4dSN5fNu9uyWRQ+IMHCllD9dBcsh+e79sOepc+xT8l28g==
"@changesets/types@^5.0.0":
version "5.0.0"
resolved "https://registry.npmjs.org/@changesets/types/-/types-5.0.0.tgz#d5eb52d074bc0358ce47d54bca54370b907812a0"
integrity sha512-IT1kBLSbAgTS4WtpU6P5ko054hq12vk4tgeIFRVE7Vnm4a/wgbNvBalgiKP0MjEXbCkZbItiGQHkCGxYWR55sA==
"@changesets/write@^0.1.8":
version "0.1.8"
resolved "https://registry.npmjs.org/@changesets/write/-/write-0.1.8.tgz#feed408f644c496bc52afc4dd1353670b4152ecb"
integrity sha512-oIHeFVMuP6jf0TPnKPpaFpvvAf3JBc+s2pmVChbeEgQTBTALoF51Z9kqxQfG4XONZPHZnqkmy564c7qohhhhTQ==
dependencies:
"@babel/runtime" "^7.10.4"
"@changesets/types" "^4.1.0"
"@changesets/types" "^5.0.0"
fs-extra "^7.0.1"
human-id "^1.0.2"
prettier "^1.19.1"
@@ -2218,21 +2224,6 @@
qs "^6.10.1"
xcase "^2.0.1"
"@google-cloud/common@^3.8.1":
version "3.10.0"
resolved "https://registry.npmjs.org/@google-cloud/common/-/common-3.10.0.tgz#454d1155bb512109cd83c6183aabbd39f9aabda7"
integrity sha512-XMbJYMh/ZSaZnbnrrOFfR/oQrb0SxG4qh6hDisWCoEbFcBHV0qHQo4uXfeMCzolx2Mfkh6VDaOGg+hyJsmxrlw==
dependencies:
"@google-cloud/projectify" "^2.0.0"
"@google-cloud/promisify" "^2.0.0"
arrify "^2.0.1"
duplexify "^4.1.1"
ent "^2.2.0"
extend "^3.0.2"
google-auth-library "^7.14.0"
retry-request "^4.2.2"
teeny-request "^7.0.0"
"@google-cloud/container@^3.0.0":
version "3.0.0"
resolved "https://registry.npmjs.org/@google-cloud/container/-/container-3.0.0.tgz#b28d086152ac19f22f2574b7e0a39774379fb885"
@@ -2269,12 +2260,12 @@
integrity sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==
"@google-cloud/storage@^5.6.0", "@google-cloud/storage@^5.8.0":
version "5.18.3"
resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.18.3.tgz#becacb909b2abf4bb54500a0efd65ceb51ef8eab"
integrity sha512-573qJ0ECoy3nkY5YaMWcVf4/46n/zdvfNgAyjaLQywl/eL38uxDhs7YVJd3pcgslaMUwKKsd/eD3St+Pq2iPew==
version "5.19.1"
resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.19.1.tgz#7252c4eb65e6014bf525a76f3af0774caa3a946d"
integrity sha512-bRTf/AD00+lPTamJdpihXC3AFtAnJFWNh/zQAor972VpuATF7u4V1anwWp0V6rKuKE3BwNM+xWxuuW/nAwEgTA==
dependencies:
"@google-cloud/common" "^3.8.1"
"@google-cloud/paginator" "^3.0.7"
"@google-cloud/projectify" "^2.0.0"
"@google-cloud/promisify" "^2.0.0"
abort-controller "^3.0.0"
arrify "^2.0.0"
@@ -2283,23 +2274,26 @@
configstore "^5.0.0"
date-and-time "^2.0.0"
duplexify "^4.0.0"
ent "^2.2.0"
extend "^3.0.2"
gaxios "^4.0.0"
get-stream "^6.0.0"
google-auth-library "^7.0.0"
google-auth-library "^7.14.1"
hash-stream-validation "^0.2.2"
mime "^3.0.0"
mime-types "^2.0.8"
p-limit "^3.0.1"
pumpify "^2.0.0"
retry-request "^4.2.2"
snakeize "^0.1.0"
stream-events "^1.0.4"
teeny-request "^7.1.3"
xdg-basedir "^4.0.0"
"@graphiql/toolkit@^0.4.2":
version "0.4.2"
resolved "https://registry.npmjs.org/@graphiql/toolkit/-/toolkit-0.4.2.tgz#34de819add64672f3f7d4830dffb2094fb8d5366"
integrity sha512-14uG67QrONbRrhXwvBJFsMfcQfexmGhj7dgkputesx9xuPUkcCDNmVULnVA8sGYt8P/rSvjkfQYx3rtfW+GhAQ==
"@graphiql/toolkit@^0.4.3":
version "0.4.3"
resolved "https://registry.npmjs.org/@graphiql/toolkit/-/toolkit-0.4.3.tgz#2653d045693d902f7faaf87b9047e8786397ec48"
integrity sha512-L0l6BezvTXmWZhtdmZxirhYwKzcZToAciQY0A13KRAWSpJ9bb/ZdkBpcz3fOXrsjuJHf0wBr6Vk9kztJ6lV7uA==
dependencies:
"@n1ru4l/push-pull-async-iterable-iterator" "^3.1.0"
meros "^1.1.4"
@@ -3482,6 +3476,11 @@
underscore "^1.9.1"
ws "^7.3.1"
"@leichtgewicht/ip-codec@^2.0.1":
version "2.0.3"
resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz#0300943770e04231041a51bd39f0439b5c7ab4f0"
integrity sha512-nkalE/f1RvRGChwBnEIoBfSEYOXnCRdleKuv6+lePbMDrMZXeDQnqak5XDOeBgrPPyPfAdcCu/B5z+v3VhplGg==
"@lerna/add@4.0.0":
version "4.0.0"
resolved "https://registry.npmjs.org/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f"
@@ -4336,48 +4335,39 @@
dependencies:
"@types/gapi.client" "*"
"@microsoft/api-documenter@^7.17.0":
version "7.17.0"
resolved "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.17.0.tgz#7d2b7775b37775fc7491b94cb5fa13ee8271fcc3"
integrity sha512-rIZcfDkBsWsflvN1n0tVgIUdivV7slBIsN0WyK8gUG1MjK7dv+rbAB5QPLhg3dPvbXLgPLanJCFwoO2yFdxs+A==
"@microsoft/api-documenter@^7.17.5":
version "7.17.5"
resolved "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.17.5.tgz#cdaec54ad04d66b6f4c6f797f4120ff8c844a82d"
integrity sha512-zyJS3qORQP33S8EU0YPZb/JLIbMoslzj1bg8VmgfYCnxhX+3/FmlrTqDCwA9h2TR4bu1r7aQ26AfjUbBUz9+EA==
dependencies:
"@microsoft/api-extractor-model" "7.16.0"
"@microsoft/api-extractor-model" "7.16.1"
"@microsoft/tsdoc" "0.13.2"
"@rushstack/node-core-library" "3.45.1"
"@rushstack/ts-command-line" "4.10.7"
"@rushstack/node-core-library" "3.45.2"
"@rushstack/ts-command-line" "4.10.8"
colors "~1.2.1"
js-yaml "~3.13.1"
resolve "~1.17.0"
"@microsoft/api-extractor-model@7.15.3":
version "7.15.3"
resolved "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.15.3.tgz#cf76deeeb2733d974da678f530c2dbaceb18a065"
integrity sha512-NkSjolmSI7NGvbdz0Y7kjQfdpD+j9E5CwXTxEyjDqxd10MI7GXV8DnAsQ57GFJcgHKgTjf2aUnYfMJ9w3aMicw==
"@microsoft/api-extractor-model@7.16.1", "@microsoft/api-extractor-model@^7.16.1":
version "7.16.1"
resolved "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.16.1.tgz#f6e53614e90c8a6be47bad5b91d49cc6d8c5c2bf"
integrity sha512-+1mlvy/ji+mUuH7WdVZ6fTo/aCKfS6m37aAFVOFWLfkMvmR+I9SjPfiv9qOg83If7GOrk2HPiHHibv6kA80VTg==
dependencies:
"@microsoft/tsdoc" "0.13.2"
"@microsoft/tsdoc-config" "~0.15.2"
"@rushstack/node-core-library" "3.45.0"
"@rushstack/node-core-library" "3.45.2"
"@microsoft/api-extractor-model@7.16.0", "@microsoft/api-extractor-model@^7.16.0":
version "7.16.0"
resolved "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.16.0.tgz#3db7360897115f26a857f1f684fb5af82b0ef9f6"
integrity sha512-0FOrbNIny8mzBrzQnSIkEjAXk0JMSnPmWYxt3ZDTPVg9S8xIPzB6lfgTg9+Mimu0RKCpGKBpd+v2WcR5vGzyUQ==
"@microsoft/api-extractor@^7.21.2":
version "7.21.2"
resolved "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.21.2.tgz#0f524e7e1cf7b000924772fb8f6b5f60cf42fba8"
integrity sha512-m0+YPaXVou01O/V9swugZG7Gn4mw6HSWY+uisf0j2JPRZcoEDyoYe4hg0ERKXOEf0hByOnMLT28nQ82v8ig9Yw==
dependencies:
"@microsoft/api-extractor-model" "7.16.1"
"@microsoft/tsdoc" "0.13.2"
"@microsoft/tsdoc-config" "~0.15.2"
"@rushstack/node-core-library" "3.45.1"
"@microsoft/api-extractor@^7.19.4":
version "7.19.4"
resolved "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.19.4.tgz#95d43d410a1dfb28a02062c4693bcb9c52afe9eb"
integrity sha512-iehC6YA3DGJvxTUaK7HUtQmP6hAQU07+Q/OR8TG4dVR6KpqCi9UPEVk8AgCvQkiK+6FbVEFQTx0qLuYk4EeuHg==
dependencies:
"@microsoft/api-extractor-model" "7.15.3"
"@microsoft/tsdoc" "0.13.2"
"@microsoft/tsdoc-config" "~0.15.2"
"@rushstack/node-core-library" "3.45.0"
"@rushstack/rig-package" "0.3.7"
"@rushstack/ts-command-line" "4.10.6"
"@rushstack/node-core-library" "3.45.2"
"@rushstack/rig-package" "0.3.9"
"@rushstack/ts-command-line" "4.10.8"
colors "~1.2.1"
lodash "~4.17.15"
resolve "~1.17.0"
@@ -4391,9 +4381,9 @@
integrity sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==
"@microsoft/microsoft-graph-types@^2.6.0":
version "2.16.0"
resolved "https://registry.npmjs.org/@microsoft/microsoft-graph-types/-/microsoft-graph-types-2.16.0.tgz#5329890d230c4fdd9f57f39e26dfade0882c94f3"
integrity sha512-Qvxv9mpXb/F4xlESEkSLjREHj3dAixTkH3LVO6Ct6sllc5RWrQxPxaSGqW9IpcLU6jI49f2XNSGLotVef3Irdg==
version "2.18.0"
resolved "https://registry.npmjs.org/@microsoft/microsoft-graph-types/-/microsoft-graph-types-2.18.0.tgz#b2ebdd55c149dfc11cca41cbf1f2082a56242450"
integrity sha512-cWiK0oaz+RrcL6EKfBeoai28L8jWJ1n+nS5cGUgRR4+POk5W5/8DV3Vs3gUNaFwmAqfYrhskyb/SNyytgAC78Q==
"@microsoft/tsdoc-config@~0.15.2":
version "0.15.2"
@@ -4405,11 +4395,16 @@
jju "~1.4.0"
resolve "~1.19.0"
"@microsoft/tsdoc@0.13.2", "@microsoft/tsdoc@^0.13.2":
"@microsoft/tsdoc@0.13.2":
version "0.13.2"
resolved "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz#3b0efb6d3903bd49edb073696f60e90df08efb26"
integrity sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg==
"@microsoft/tsdoc@^0.14.1":
version "0.14.1"
resolved "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz#155ef21065427901994e765da8a0ba0eaae8b8bd"
integrity sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw==
"@mswjs/cookies@^0.1.6", "@mswjs/cookies@^0.1.7":
version "0.1.7"
resolved "https://registry.npmjs.org/@mswjs/cookies/-/cookies-0.1.7.tgz#d334081b2c51057a61c1dd7b76ca3cac02251651"
@@ -5229,10 +5224,10 @@
estree-walker "^2.0.1"
picomatch "^2.2.2"
"@rushstack/node-core-library@3.45.0":
version "3.45.0"
resolved "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.0.tgz#8c86b39271b6d84260b1e70db87e1e265b54f620"
integrity sha512-YMuIJl19vQT1+g/OU9mLY6T5ZBT9uDlmeXExDQACpGuxTJW+LHNbk/lRX+eCApQI2eLBlaL4U68r3kZlqwbdmw==
"@rushstack/node-core-library@3.45.2":
version "3.45.2"
resolved "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.2.tgz#68fc89c5bea4007359fa4ff203bf3eca27037f40"
integrity sha512-MJKdB6mxOoIkks3htGVCo7aiTzllm2I6Xua+KbTSb0cp7rBp8gTCOF/4d8R4HFMwpRdEdwzKgqMM6k9rAK73iw==
dependencies:
"@types/node" "12.20.24"
colors "~1.2.1"
@@ -5244,43 +5239,18 @@
timsort "~0.3.0"
z-schema "~5.0.2"
"@rushstack/node-core-library@3.45.1":
version "3.45.1"
resolved "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.1.tgz#787361b61a48d616eb4b059641721a3dc138f001"
integrity sha512-BwdssTNe007DNjDBxJgInHg8ePytIPyT0La7ZZSQZF9+rSkT42AygXPGvbGsyFfEntjr4X37zZSJI7yGzL16cQ==
dependencies:
"@types/node" "12.20.24"
colors "~1.2.1"
fs-extra "~7.0.1"
import-lazy "~4.0.0"
jju "~1.4.0"
resolve "~1.17.0"
semver "~7.3.0"
timsort "~0.3.0"
z-schema "~5.0.2"
"@rushstack/rig-package@0.3.7":
version "0.3.7"
resolved "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.7.tgz#3fa564b1d129d28689dd4309502792b15e84bf81"
integrity sha512-pzMsTSeTC8IiZ6EJLr53gGMvhT4oLWH+hxD7907cHyWuIUlEXFtu/2pK25vUQT13nKp5DJCWxXyYoGRk/h6rtA==
"@rushstack/rig-package@0.3.9":
version "0.3.9"
resolved "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.9.tgz#5e10ada5a8348f886b6ebe3eed436492d6ccf70c"
integrity sha512-z3Oxpfb4n9mGXwseX+ifpkmUf9B8Fy8oieVwg8eFgpCbzllkgOwEiwLKEnRWVQ8owFcd46NCKz+7ICH35CRsAw==
dependencies:
resolve "~1.17.0"
strip-json-comments "~3.1.1"
"@rushstack/ts-command-line@4.10.6":
version "4.10.6"
resolved "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.6.tgz#5669e481e4339ceb4e1428183eb0937d3bc3841b"
integrity sha512-Y3GkUag39sTIlukDg9mUp8MCHrrlJ27POrBNRQGc/uF+VVgX8M7zMzHch5zP6O1QVquWgD7Engdpn2piPYaS/g==
dependencies:
"@types/argparse" "1.0.38"
argparse "~1.0.9"
colors "~1.2.1"
string-argv "~0.3.1"
"@rushstack/ts-command-line@4.10.7":
version "4.10.7"
resolved "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.7.tgz#21e3757a756cbd4f7eeab8f89ec028a64d980efc"
integrity sha512-CjS+DfNXUSO5Ab2wD1GBGtUTnB02OglRWGqfaTcac9Jn45V5MeUOsq/wA8wEeS5Y/3TZ2P1k+IWdVDiuOFP9Og==
"@rushstack/ts-command-line@4.10.8":
version "4.10.8"
resolved "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.8.tgz#f4bec690e7f4838e6f91028d8a557e67c3a75f68"
integrity sha512-G7CQYY/m3aZU5fVxbebv35yDeua7sSumrDAB2pJp0d60ZEsxGkUQW8771CeMcGWwSKqT9PxPzKpmIakiWv54sA==
dependencies:
"@types/argparse" "1.0.38"
argparse "~1.0.9"
@@ -5546,9 +5516,9 @@
pretty-format "^27.0.2"
"@testing-library/jest-dom@^5.10.1":
version "5.16.3"
resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.3.tgz#b76851a909586113c20486f1679ffb4d8ec27bfa"
integrity sha512-u5DfKj4wfSt6akfndfu1eG06jsdyA/IUrlX2n3pyq5UXgXMhXY+NJb8eNK/7pqPWAhCKsCGWDdDO0zKMKAYkEA==
version "5.16.4"
resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.4.tgz#938302d7b8b483963a3ae821f1c0808f872245cd"
integrity sha512-Gy+IoFutbMQcky0k+bqqumXZ1cTGswLsFqmNLzNdSKkU9KGV2u9oXhukCbbJ9/LRPKiqwxEE8VpV/+YZlfkPUA==
dependencies:
"@babel/runtime" "^7.9.2"
"@types/testing-library__jest-dom" "^5.9.1"
@@ -6229,10 +6199,10 @@
dependencies:
"@types/node" "*"
"@types/jest-when@^2.7.2":
version "2.7.2"
resolved "https://registry.npmjs.org/@types/jest-when/-/jest-when-2.7.2.tgz#619fbc5f623bcd0b29efde0e4993c7f0d50d026d"
integrity sha512-vOtj0cev6vO1VX7Jbfg/qvy+sfLI64STsHbKVkggK+1kd11rcMGzFpZKBxUvQfsm4JRULCBISu+qrfs7fYZFGg==
"@types/jest-when@^3.5.0":
version "3.5.0"
resolved "https://registry.npmjs.org/@types/jest-when/-/jest-when-3.5.0.tgz#6a573cd521da131e6801f0991b4f1d4dee2ebab5"
integrity sha512-rNUuZ3Mn/HDzpImPXDeOtW18zqyerPoOS2aKU0zUFbirWgJ7sN7LnRv73RmbBQ/uzw28sxf/nUofxbhJ5DWB0w==
dependencies:
"@types/jest" "*"
@@ -6330,9 +6300,9 @@
"@types/node" "*"
"@types/lodash@^4.14.151", "@types/lodash@^4.14.173", "@types/lodash@^4.14.175":
version "4.14.178"
resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz#341f6d2247db528d4a13ddbb374bcdc80406f4f8"
integrity sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==
version "4.14.181"
resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.181.tgz#d1d3740c379fda17ab175165ba04e2d03389385d"
integrity sha512-n3tyKthHJbkiWhDZs3DkhkCzt2MexYHXlX0td5iMplyfwketaOeKboEVBqzceH7juqvEg3q5oUoBFxSLu7zFag==
"@types/long@^4.0.0", "@types/long@^4.0.1":
version "4.0.1"
@@ -6835,9 +6805,9 @@
"@types/node" "*"
"@types/supertest@^2.0.8":
version "2.0.11"
resolved "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.11.tgz#2e70f69f220bc77b4f660d72c2e1a4231f44a77d"
integrity sha512-uci4Esokrw9qGb9bvhhSVEjd6rkny/dk5PK/Qz4yxKiyppEI+dOPlNrZBahE3i+PoKFYyDxChVXZ/ysS/nrm1Q==
version "2.0.12"
resolved "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.12.tgz#ddb4a0568597c9aadff8dbec5b2e8fddbe8692fc"
integrity sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==
dependencies:
"@types/superagent" "*"
@@ -6981,10 +6951,10 @@
dependencies:
"@types/node" "*"
"@types/ws@^8.0.0", "@types/ws@^8.2.2":
version "8.2.2"
resolved "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz#7c5be4decb19500ae6b3d563043cd407bf366c21"
integrity sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==
"@types/ws@^8.0.0", "@types/ws@^8.5.1":
version "8.5.3"
resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d"
integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==
dependencies:
"@types/node" "*"
@@ -7545,9 +7515,9 @@ ansi-regex@^2.0.0:
integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
ansi-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
version "3.0.1"
resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1"
integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==
ansi-regex@^4.1.0:
version "4.1.0"
@@ -7852,7 +7822,7 @@ array-flatten@1.1.1:
resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
array-flatten@^2.1.0:
array-flatten@^2.1.2:
version "2.1.2"
resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099"
integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==
@@ -8509,17 +8479,15 @@ body-parser@1.19.2, body-parser@^1.19.0:
raw-body "2.4.3"
type-is "~1.6.18"
bonjour@^3.5.0:
version "3.5.0"
resolved "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5"
integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU=
bonjour-service@^1.0.11:
version "1.0.11"
resolved "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.11.tgz#5418e5c1ac91c89a406f853a942e7892829c0d89"
integrity sha512-drMprzr2rDTCtgEE3VgdA9uUFaUHF+jXduwYSThHJnKMYM+FhI9Z3ph+TX3xy0LtgYHae6CHYPJ/2UnK8nQHcA==
dependencies:
array-flatten "^2.1.0"
deep-equal "^1.0.1"
array-flatten "^2.1.2"
dns-equal "^1.0.0"
dns-txt "^2.0.2"
multicast-dns "^6.0.1"
multicast-dns-service-types "^1.1.0"
fast-deep-equal "^3.1.3"
multicast-dns "^7.2.4"
boolbase@^1.0.0:
version "1.0.0"
@@ -8726,11 +8694,6 @@ buffer-indexof-polyfill@~1.0.0:
resolved "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c"
integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==
buffer-indexof@^1.0.0:
version "1.1.1"
resolved "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c"
integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==
buffer-writer@2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04"
@@ -10863,18 +10826,6 @@ dedent@^0.7.0:
resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=
deep-equal@^1.0.1:
version "1.1.1"
resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==
dependencies:
is-arguments "^1.0.4"
is-date-object "^1.0.1"
is-regex "^1.0.4"
object-is "^1.0.1"
object-keys "^1.1.1"
regexp.prototype.flags "^1.2.0"
deep-extend@0.6.0, deep-extend@^0.6.0:
version "0.6.0"
resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
@@ -11149,20 +11100,12 @@ dns-equal@^1.0.0:
resolved "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0=
dns-packet@^1.3.1:
version "1.3.4"
resolved "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f"
integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==
dns-packet@^5.2.2:
version "5.3.1"
resolved "https://registry.npmjs.org/dns-packet/-/dns-packet-5.3.1.tgz#eb94413789daec0f0ebe2fcc230bdc9d7c91b43d"
integrity sha512-spBwIj0TK0Ey3666GwIdWVfUpLyubpU53BTCu8iPn4r4oXd9O14Hjg3EHw3ts2oed77/SeckunUYCyRlSngqHw==
dependencies:
ip "^1.1.0"
safe-buffer "^5.0.1"
dns-txt@^2.0.2:
version "2.0.2"
resolved "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6"
integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=
dependencies:
buffer-indexof "^1.0.0"
"@leichtgewicht/ip-codec" "^2.0.1"
docker-compose@^0.23.17:
version "0.23.17"
@@ -12178,6 +12121,7 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
"@backstage/app-defaults" "^1.0.1-next.1"
"@backstage/catalog-model" "^1.0.1-next.0"
"@backstage/cli" "^0.17.0-next.1"
"@backstage/config" "^1.0.0"
"@backstage/core-app-api" "^1.0.1-next.0"
"@backstage/core-components" "^0.9.3-next.0"
"@backstage/core-plugin-api" "^1.0.0"
@@ -12216,8 +12160,10 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
"@backstage/plugin-scaffolder" "^1.0.1-next.1"
"@backstage/plugin-search" "^0.7.5-next.0"
"@backstage/plugin-search-common" "^0.3.3-next.1"
"@backstage/plugin-search-react" "^0.0.0"
"@backstage/plugin-sentry" "^0.3.42-next.0"
"@backstage/plugin-shortcuts" "^0.2.5-next.0"
"@backstage/plugin-stack-overflow" "^0.1.0-next.0"
"@backstage/plugin-tech-insights" "^0.1.14-next.0"
"@backstage/plugin-tech-radar" "^0.5.11-next.1"
"@backstage/plugin-techdocs" "^1.0.1-next.1"
@@ -12388,7 +12334,7 @@ express-xml-bodyparser@^0.3.0:
dependencies:
xml2js "^0.4.11"
express@^4.17.1:
express@^4.17.1, express@^4.17.3:
version "4.17.3"
resolved "https://registry.npmjs.org/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1"
integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==
@@ -13459,7 +13405,7 @@ globby@^7.1.1:
pify "^3.0.0"
slash "^1.0.0"
google-auth-library@^7.0.0, google-auth-library@^7.14.0, google-auth-library@^7.6.1:
google-auth-library@^7.14.1, google-auth-library@^7.6.1:
version "7.14.1"
resolved "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.1.tgz#e3483034162f24cc71b95c8a55a210008826213c"
integrity sha512-5Rk7iLNDFhFeBYc3s8l1CqzbEBcdhwR193RlD4vSNFajIcINKI8W8P0JLmBpwymHqqWbX34pJDQu39cSy/6RsA==
@@ -13545,11 +13491,11 @@ grapheme-splitter@^1.0.4:
integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
graphiql@^1.5.12:
version "1.8.3"
resolved "https://registry.npmjs.org/graphiql/-/graphiql-1.8.3.tgz#4696755cbba851c93aea9deacbcf4229044a705d"
integrity sha512-3qb3jdlzg8nqQCRMnch6lG11royrE6etP2eoDKLxdrSpdaxI0PgNNttbMAJJYfsqEJ0XHcXtUDt1jH6jkKOK4Q==
version "1.8.4"
resolved "https://registry.npmjs.org/graphiql/-/graphiql-1.8.4.tgz#3a447eae85d839eb207a07fc3aabc6f3a10df46d"
integrity sha512-mEYtfympvlZ4E1VMEMZ0BG3NCLA9L69EEbnpHpAA12oWz3aul4HXWl59JS0aHZftyiPx1/gHjSsjctkhbazT9g==
dependencies:
"@graphiql/toolkit" "^0.4.2"
"@graphiql/toolkit" "^0.4.3"
codemirror "^5.58.2"
codemirror-graphql "^1.2.15"
copy-to-clipboard "^3.2.0"
@@ -14069,7 +14015,7 @@ http-parser-js@>=0.5.1:
resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9"
integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==
http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1:
http-proxy-agent@^4.0.1:
version "4.0.1"
resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a"
integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==
@@ -14087,7 +14033,7 @@ http-proxy-agent@^5.0.0:
agent-base "6"
debug "4"
http-proxy-middleware@^2.0.0:
http-proxy-middleware@^2.0.0, http-proxy-middleware@^2.0.3:
version "2.0.4"
resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.4.tgz#03af0f4676d172ae775cb5c33f592f40e1a4e07a"
integrity sha512-m/4FxX17SUvz4lJ5WPXOHDUuCwIqXLfLHs1s0uZ3oYjhoXlx9csYxaOa0ElDEJ+h8Q4iJ1s+lTMbiCa4EXIJqg==
@@ -14243,6 +14189,13 @@ ignore-walk@^4.0.1:
dependencies:
minimatch "^3.0.4"
ignore-walk@^5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz#5f199e23e1288f518d90358d461387788a154776"
integrity sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==
dependencies:
minimatch "^5.0.1"
ignore@^3.3.5:
version "3.3.10"
resolved "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043"
@@ -14519,7 +14472,7 @@ ioredis@^4.28.5:
redis-parser "^3.0.0"
standard-as-callback "^2.1.0"
ip@^1.1.0, ip@^1.1.5:
ip@^1.1.5:
version "1.1.5"
resolved "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=
@@ -14960,7 +14913,7 @@ is-reference@^1.2.1:
dependencies:
"@types/estree" "*"
is-regex@^1.0.4, is-regex@^1.1.4:
is-regex@^1.1.4:
version "1.1.4"
resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
@@ -17976,7 +17929,7 @@ minimatch@3.0.4:
dependencies:
brace-expansion "^1.1.7"
minimatch@5.0.1, minimatch@^5.0.0:
minimatch@5.0.1, minimatch@^5.0.0, minimatch@^5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b"
integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==
@@ -18162,9 +18115,9 @@ moment-timezone@^0.5.x:
moment ">= 2.9.0"
"moment@>= 2.9.0", moment@>=2.14.0, moment@^2.27.0, moment@^2.29.1:
version "2.29.1"
resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
version "2.29.2"
resolved "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz#00910c60b20843bcba52d37d58c628b47b1f20e4"
integrity sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==
morgan@^1.10.0:
version "1.10.0"
@@ -18254,17 +18207,12 @@ msw@^0.36.3:
type-fest "^1.2.2"
yargs "^17.3.0"
multicast-dns-service-types@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901"
integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=
multicast-dns@^6.0.1:
version "6.2.3"
resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229"
integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==
multicast-dns@^7.2.4:
version "7.2.4"
resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.4.tgz#cf0b115c31e922aeb20b64e6556cbeb34cf0dd19"
integrity sha512-XkCYOU+rr2Ft3LI6w4ye51M3VK31qJXFIxu0XLw169PtKG0Zx47OrXeVW/GCYOfpC9s1yyyf1S+L8/4LY0J9Zw==
dependencies:
dns-packet "^1.3.1"
dns-packet "^5.2.2"
thunky "^1.0.2"
multimatch@^5.0.0:
@@ -18453,10 +18401,10 @@ node-fetch@2.6.7, node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-
dependencies:
whatwg-url "^5.0.0"
node-forge@^1.0.0, node-forge@^1.2.0:
version "1.3.0"
resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz#37a874ea723855f37db091e6c186e5b67a01d4b2"
integrity sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA==
node-forge@^1, node-forge@^1.0.0:
version "1.3.1"
resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==
node-gyp@^5.0.2:
version "5.1.0"
@@ -18641,10 +18589,10 @@ normalize-url@^6.0.1:
resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
npm-bundled@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b"
integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==
npm-bundled@^1.1.1, npm-bundled@^1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1"
integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==
dependencies:
npm-normalize-package-bin "^1.0.1"
@@ -18703,6 +18651,16 @@ npm-packlist@^3.0.0:
npm-bundled "^1.1.1"
npm-normalize-package-bin "^1.0.1"
npm-packlist@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.0.0.tgz#74795ebbbf91bd5a2db6ecff4d6fe1f1c1a07e11"
integrity sha512-uU20UwM4Hogfab1Q7htJbhcyafM9lGHxOrDjkKvR2S3z7Ds0uRaESk0cXctczk+ABT4DZWNwjB10xlurFdEwZg==
dependencies:
glob "^7.2.0"
ignore-walk "^5.0.1"
npm-bundled "^1.1.2"
npm-normalize-package-bin "^1.0.1"
npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.0, npm-pick-manifest@^6.1.1:
version "6.1.1"
resolved "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148"
@@ -18853,11 +18811,6 @@ object-inspect@^1.11.0, object-inspect@^1.12.0, object-inspect@^1.9.0:
resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0"
integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==
object-is@^1.0.1:
version "1.0.2"
resolved "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4"
integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==
object-keys@^1.0.12, object-keys@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
@@ -20997,9 +20950,9 @@ react-helmet@6.1.0:
react-side-effect "^2.1.0"
react-hook-form@^7.12.2, react-hook-form@^7.13.0:
version "7.28.1"
resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.28.1.tgz#95fc37be6c6b9d57212eb7eca055fb36d079b3b7"
integrity sha512-mgwxvXuvt3FMY/mdnWbPc++Zf1U5xYzkhOaL05mtFMLvXc9MvUhMUlKtUVuO12sOrgT3nPXBgVFawtiJ4ONrgg==
version "7.29.0"
resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.29.0.tgz#5e7e41a483b70731720966ed8be52163ea1fecf1"
integrity sha512-NcJqWRF6el5HMW30fqZRt27s+lorvlCCDbTpAyHoodQeYWXgQCvZJJQLC1kRMKdrJknVH0NIg3At6TUzlZJFOQ==
react-hot-loader@^4.13.0:
version "4.13.0"
@@ -21618,7 +21571,7 @@ regex-not@^1.0.0, regex-not@^1.0.2:
extend-shallow "^3.0.2"
safe-regex "^1.1.0"
regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.1:
regexp.prototype.flags@^1.3.1:
version "1.4.1"
resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307"
integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==
@@ -22287,12 +22240,12 @@ select-hose@^2.0.0:
resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=
selfsigned@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz#e927cd5377cbb0a1075302cff8df1042cc2bce5b"
integrity sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==
selfsigned@^2.0.0, selfsigned@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz#8b2df7fa56bf014d19b6007655fff209c0ef0a56"
integrity sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==
dependencies:
node-forge "^1.2.0"
node-forge "^1"
semver-diff@^3.1.1:
version "3.1.1"
@@ -23329,7 +23282,7 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^7.0.0, strip-ansi@^7.0.1:
strip-ansi@^7.0.1:
version "7.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==
@@ -23773,12 +23726,12 @@ tdigest@^0.1.1:
react-router-dom "6.0.0-beta.0"
react-use "^17.2.4"
teeny-request@^7.0.0:
version "7.0.1"
resolved "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz#bdd41fdffea5f8fbc0d29392cb47bec4f66b2b4c"
integrity sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==
teeny-request@^7.1.3:
version "7.2.0"
resolved "https://registry.npmjs.org/teeny-request/-/teeny-request-7.2.0.tgz#41347ece068f08d741e7b86df38a4498208b2633"
integrity sha512-SyY0pek1zWsi0LRVAALem+avzMLc33MKW/JLLakdP4s9+D7+jHcy5x6P+h94g2QNZsAqQNfX5lsbd3WSeJXrrw==
dependencies:
http-proxy-agent "^4.0.0"
http-proxy-agent "^5.0.0"
https-proxy-agent "^5.0.0"
node-fetch "^2.6.1"
stream-events "^1.0.5"
@@ -25178,38 +25131,37 @@ webpack-dev-middleware@^5.3.1:
schema-utils "^4.0.0"
webpack-dev-server@^4.7.3:
version "4.7.4"
resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.4.tgz#d0ef7da78224578384e795ac228d8efb63d5f945"
integrity sha512-nfdsb02Zi2qzkNmgtZjkrMOcXnYZ6FLKcQwpxT7MvmHKc+oTtDsBju8j+NMyAygZ9GW1jMEUpy3itHtqgEhe1A==
version "4.8.1"
resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.8.1.tgz#58f9d797710d6e25fa17d6afab8708f958c11a29"
integrity sha512-dwld70gkgNJa33czmcj/PlKY/nOy/BimbrgZRaR9vDATBQAYgLzggR0nxDtPLJiLrMgZwbE6RRfJ5vnBBasTyg==
dependencies:
"@types/bonjour" "^3.5.9"
"@types/connect-history-api-fallback" "^1.3.5"
"@types/express" "^4.17.13"
"@types/serve-index" "^1.9.1"
"@types/sockjs" "^0.3.33"
"@types/ws" "^8.2.2"
"@types/ws" "^8.5.1"
ansi-html-community "^0.0.8"
bonjour "^3.5.0"
bonjour-service "^1.0.11"
chokidar "^3.5.3"
colorette "^2.0.10"
compression "^1.7.4"
connect-history-api-fallback "^1.6.0"
default-gateway "^6.0.3"
del "^6.0.0"
express "^4.17.1"
express "^4.17.3"
graceful-fs "^4.2.6"
html-entities "^2.3.2"
http-proxy-middleware "^2.0.0"
http-proxy-middleware "^2.0.3"
ipaddr.js "^2.0.1"
open "^8.0.9"
p-retry "^4.5.0"
portfinder "^1.0.28"
rimraf "^3.0.2"
schema-utils "^4.0.0"
selfsigned "^2.0.0"
selfsigned "^2.0.1"
serve-index "^1.9.1"
sockjs "^0.3.21"
spdy "^4.0.2"
strip-ansi "^7.0.0"
webpack-dev-middleware "^5.3.1"
ws "^8.4.2"