diff --git a/.changeset/breezy-dolphins-lay.md b/.changeset/breezy-dolphins-lay.md new file mode 100644 index 0000000000..0197979531 --- /dev/null +++ b/.changeset/breezy-dolphins-lay.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +AWS-ALB: update provider to the latest changes described [here](https://backstage.io/docs/auth/identity-resolver). + +This removes the `ExperimentalIdentityResolver` type in favor of `SignInResolver` and `AuthHandler`. +The AWS ALB provider can now be configured in the same way as the Google provider in the example. diff --git a/.changeset/dirty-balloons-develop.md b/.changeset/dirty-balloons-develop.md new file mode 100644 index 0000000000..6dd9005cca --- /dev/null +++ b/.changeset/dirty-balloons-develop.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-api-docs': patch +--- + +Bump `swagger-ui-react` to `^4.0.0-rc.3`. diff --git a/.changeset/happy-hotels-explain.md b/.changeset/happy-hotels-explain.md new file mode 100644 index 0000000000..ede18c3187 --- /dev/null +++ b/.changeset/happy-hotels-explain.md @@ -0,0 +1,7 @@ +--- +'@backstage/core-app-api': patch +'@backstage/core-components': patch +'@backstage/theme': patch +--- + +Internal refactor to avoid importing all of `@material-ui/core`. diff --git a/.changeset/large-pots-invent.md b/.changeset/large-pots-invent.md new file mode 100644 index 0000000000..269b0bd288 --- /dev/null +++ b/.changeset/large-pots-invent.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Support `material-ui` overrides in SystemDiagramCard and EmptityLinksEmptyState components diff --git a/.changeset/lemon-actors-wait.md b/.changeset/lemon-actors-wait.md new file mode 100644 index 0000000000..e98adc891e --- /dev/null +++ b/.changeset/lemon-actors-wait.md @@ -0,0 +1,15 @@ +--- +'@backstage/plugin-home': patch +--- + +Adds a `` component that shows a playful greeting on the home page. +To use it, pass it to the home page header: + +```typescript + +
} pageTitleOverride="Home"> + +
+ โ€ฆ +
+``` diff --git a/.changeset/mean-eggs-knock.md b/.changeset/mean-eggs-knock.md new file mode 100644 index 0000000000..dd9336a0e7 --- /dev/null +++ b/.changeset/mean-eggs-knock.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-components': minor +'@backstage/plugin-catalog-graph': minor +--- + +Add documentation and more type safety around DependencyGraph diff --git a/.changeset/neat-cooks-sell.md b/.changeset/neat-cooks-sell.md new file mode 100644 index 0000000000..11859c3589 --- /dev/null +++ b/.changeset/neat-cooks-sell.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Fix search in Firefox. When the search was performed by pressing enter, the query parameter was first set but then reverted back. diff --git a/.changeset/search-gentle-chairs-grab.md b/.changeset/search-gentle-chairs-grab.md new file mode 100644 index 0000000000..f3e2cf3cec --- /dev/null +++ b/.changeset/search-gentle-chairs-grab.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search': patch +--- + +SearchBar component to accept optional placeholder prop diff --git a/.changeset/silent-candles-remember.md b/.changeset/silent-candles-remember.md new file mode 100644 index 0000000000..51700c4582 --- /dev/null +++ b/.changeset/silent-candles-remember.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': minor +--- + +The syntax highlighting library used by the `CodeSnippet` component is now lazy loaded. This most likely has no effect on existing code, but may break tests as the content of the `CodeSnippet` is now rendered asynchronously. diff --git a/.changeset/six-pumpkins-lie.md b/.changeset/six-pumpkins-lie.md new file mode 100644 index 0000000000..7f28582d91 --- /dev/null +++ b/.changeset/six-pumpkins-lie.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +This change refactors the internal package structure to remove the `next` catalog folder that was used during the implementation and testing phase of the new catalog engine. The implementation is now the default and is therefore restructured to no longer be packaged under `next/`. This refactor does not change catalog imports from other parts of the project. diff --git a/.changeset/smart-seahorses-ring.md b/.changeset/smart-seahorses-ring.md new file mode 100644 index 0000000000..a5ae156afd --- /dev/null +++ b/.changeset/smart-seahorses-ring.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Update default backend ESLint configuration to allow usage of `__dirname` in tests. diff --git a/.changeset/stale-seahorses-sell.md b/.changeset/stale-seahorses-sell.md new file mode 100644 index 0000000000..2fa52b109f --- /dev/null +++ b/.changeset/stale-seahorses-sell.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-azure-devops-backend': patch +--- + +Expands the Azure DevOps backend plugin to provide pull request data to be used by the front end plugin diff --git a/.changeset/two-foxes-marry.md b/.changeset/two-foxes-marry.md new file mode 100644 index 0000000000..a6ac7df666 --- /dev/null +++ b/.changeset/two-foxes-marry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Fix `` to display only the selected tab, not the other way around. diff --git a/.changeset/wise-clocks-accept.md b/.changeset/wise-clocks-accept.md new file mode 100644 index 0000000000..7b9090937b --- /dev/null +++ b/.changeset/wise-clocks-accept.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Support `material-ui` overrides in plugin-catalog-react components diff --git a/ADOPTERS.md b/ADOPTERS.md index 5fafdb5da4..c94d83e97e 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -1,57 +1,58 @@ -| Organization | Contact | Description of Use | -| ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. | -| [bol.com](https://www.bol.com) | [@sagacity](https://github.com/sagacity) | Initial work being done to unify platform tooling. | -| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | -| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | -| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | -| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. | -| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | -| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | -| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | -| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | -| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | -| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | -| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling | -| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks | -| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | -| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | -| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | -| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo), [Erik Lindgren](https://github.com/lindgren) | EG Common Developer Toolkit | -| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go | -| [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. | -| [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 teamโ€™s 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. | -| [Booz Allen Hamilton](https://www.boozallen.com/) | [Jason Miller](https://github.com/JasonMiller-BAH) | Developer portal for a full-stack software development ecosystem that accelerates consistent and repeatable Modern Software Development practices for internal innovation and investments. | -| [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 ๐ŸŒ•๐Ÿš€๐Ÿง‘โ€๐Ÿš€ | -| [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. | -| [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. | -| [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | -| [RedVentures](https://www.redventures.com/) | [Chris Diaz](https://github.com/codingdiaz) | Developer portal that brings everything an engineer needs to provide value into a single pane of glass. | -| [MavTek](https://www.mavtek.com/) | [@fgascon](https://github.com/fgascon) | Developer portal focused on standardizing practices, centralizing documentation and streamlining developer practices. | -| [QuintoAndar](https://www.quintoandar.com.br/) | [@quintoandar](https://github.com/quintoandar) | Developer portal, services catalog and centralization of service metrics. | -| [empathy.co](https://empathy.co/) | [@guillermotti](https://github.com/guillermotti) | Developer portal for tech docs, service catalog, plugin discovery and much more. | -| [creditas.com](https://creditas.com/) | [@aureliosaraiva](https://github.com/aureliosaraiva) [@Creditas](https://github.com/creditas) | Centralization of all services, standards, documentation, etc. We started the deployment process. | -| [Prisjakt](https://www.prisjakt.nu) / [PriceSpy](https://pricespy.co.uk) | [@kennylindahl](https://github.com/kennylindahl) | Internal developer portal - Documentation, scaffolding, software catalog, TechRadar, Gitlab org data integration | -| [Powerspike](https://powerspike.tv/) | [@trelore](https://github.com/trelore) | Developer portal for documentation of core libraries and repositories. | -| [2U](https://2u.com) | [Andrew Thal](https://github.com/athal7) | Development team home-base, promoting service discoverability, resource dependencies, and tech radar | -| [Taxfix](https://taxfix.de/) | [Sami Ur Rehman](https://github.com/samiurrehman92) | Developer's portal with software catalog at it's core. Hosts API Specs, Tech Docs, Tech Radar and some custom plugins. | -| [Busuu](https://busuu.com/) | [Adam Tester](https://github.com/adamtester) | Developer portal with service catalog, API docs, Event docs, service templating, and cost insights. | -| [Loadsmart](https://loadsmart.com/) | [Loadsmart](https://github.com/loadsmart) | Improve services visibility and operations for service owners and developers. | +| Organization | Contact | Description of Use | +| ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. | +| [bol.com](https://www.bol.com) | [@sagacity](https://github.com/sagacity) | Initial work being done to unify platform tooling. | +| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | +| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | +| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | +| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. | +| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | +| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | +| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | +| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | +| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | +| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | +| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling | +| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks | +| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | +| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | +| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | +| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo), [Erik Lindgren](https://github.com/lindgren) | EG Common Developer Toolkit | +| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go | +| [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. | +| [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 teamโ€™s 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. | +| [Booz Allen Hamilton](https://www.boozallen.com/) | [Jason Miller](https://github.com/JasonMiller-BAH) | Developer portal for a full-stack software development ecosystem that accelerates consistent and repeatable Modern Software Development practices for internal innovation and investments. | +| [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 ๐ŸŒ•๐Ÿš€๐Ÿง‘โ€๐Ÿš€ | +| [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. | +| [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. | +| [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | +| [RedVentures](https://www.redventures.com/) | [Chris Diaz](https://github.com/codingdiaz) | Developer portal that brings everything an engineer needs to provide value into a single pane of glass. | +| [MavTek](https://www.mavtek.com/) | [@fgascon](https://github.com/fgascon) | Developer portal focused on standardizing practices, centralizing documentation and streamlining developer practices. | +| [QuintoAndar](https://www.quintoandar.com.br/) | [@quintoandar](https://github.com/quintoandar) | Developer portal, services catalog and centralization of service metrics. | +| [empathy.co](https://empathy.co/) | [@guillermotti](https://github.com/guillermotti) | Developer portal for tech docs, service catalog, plugin discovery and much more. | +| [creditas.com](https://creditas.com/) | [@aureliosaraiva](https://github.com/aureliosaraiva) [@Creditas](https://github.com/creditas) | Centralization of all services, standards, documentation, etc. We started the deployment process. | +| [Prisjakt](https://www.prisjakt.nu) / [PriceSpy](https://pricespy.co.uk) | [@kennylindahl](https://github.com/kennylindahl) | Internal developer portal - Documentation, scaffolding, software catalog, TechRadar, Gitlab org data integration | +| [Powerspike](https://powerspike.tv/) | [@trelore](https://github.com/trelore) | Developer portal for documentation of core libraries and repositories. | +| [2U](https://2u.com) | [Andrew Thal](https://github.com/athal7) | Development team home-base, promoting service discoverability, resource dependencies, and tech radar | +| [Taxfix](https://taxfix.de/) | [Sami Ur Rehman](https://github.com/samiurrehman92) | Developer's portal with software catalog at it's core. Hosts API Specs, Tech Docs, Tech Radar and some custom plugins. | +| [Busuu](https://busuu.com/) | [Adam Tester](https://github.com/adamtester) | Developer portal with service catalog, API docs, Event docs, service templating, and cost insights. | +| [Loadsmart](https://loadsmart.com/) | [Loadsmart](https://github.com/loadsmart) | Improve services visibility and operations for service owners and developers. | | [Monzo](https://monzo.com/) | [@WillSewell](https://github.com/WillSewell), [@joechrisellis](https://github.com/joechrisellis) | Developer portal showing metadata and docs for over 2000 microservices. We have built a number of plugins such as a UI for our system to measure [software excellence](https://monzo.com/blog/2021/09/15/how-we-measure-software-excellence), and a UI to show deployment and config change events. | -| [Vaimo](https://www.vaimo.com) | [@vaimo-magnus](https://github.com/vaimo-magnus) | Developer Portal for our developers at Vaimo, currently docs and self-service towards our internal PaaS based on k8s. Plans to extend the catalog into Projects, Environments etc | -| [Wayfair](https://www.wayfair.com) | [@fransan](https://github.com/fransan), [@errskipower](https://github.com/errskipower), [@hrrs](https://github.com/hrrs) | Developer portal for service catalog, technical documentation, and APIs. | -| [CircleHD](https://www.circlehd.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | -| [CastDesk](https://castdesk.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | -| [Santagostino](https://santagostino.it) | [@santagostino](https://github.com/santagostino) | Developer portal, gateway to our infrastructure, documentation, service catalog and internal tooling. | -| [Peak](https://peak.ai) | [Luke Beamish](https://github.com/lukebeamish-peak) | Developer portal for all internal engineers to access documentation and tooling. | -| [Gelato](https://gelato.com/) | [Dmitry Makarenko](https://github.com/dmitry-makarenko-gelato) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal and third-party systems๐Ÿš€. | +| [Vaimo](https://www.vaimo.com) | [@vaimo-magnus](https://github.com/vaimo-magnus) | Developer Portal for our developers at Vaimo, currently docs and self-service towards our internal PaaS based on k8s. Plans to extend the catalog into Projects, Environments etc | +| [Wayfair](https://www.wayfair.com) | [@fransan](https://github.com/fransan), [@errskipower](https://github.com/errskipower), [@hrrs](https://github.com/hrrs) | Developer portal for service catalog, technical documentation, and APIs. | +| [CircleHD](https://www.circlehd.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | +| [CastDesk](https://castdesk.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | +| [Santagostino](https://santagostino.it) | [@santagostino](https://github.com/santagostino) | Developer portal, gateway to our infrastructure, documentation, service catalog and internal tooling. | +| [Peak](https://peak.ai) | [Luke Beamish](https://github.com/lukebeamish-peak) | Developer portal for all internal engineers to access documentation and tooling. | +| [Gelato](https://gelato.com/) | [Dmitry Makarenko](https://github.com/dmitry-makarenko-gelato) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal and third-party systems๐Ÿš€. | +| [GoCardless](https://gocardless.com/) | [James Turley](https://github.com/tragiclifestories) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal systems. | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5242309eee..99d63cf0b4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -129,9 +129,10 @@ In general, changesets are not needed for the documentation, build utilities, co 1. Run `yarn changeset` 2. Select which packages you want to include a changeset for 3. Select impact of change that you're introducing, using `minor` for breaking changes and `patch` otherwise. We do not use `major` changes while packages are at version `0.x`. -4. Add generated changeset to Git -5. Push the commit with your changeset to the branch associated with your PR -6. Accept our gratitude for making the release process easier on the maintainers +4. Explain your changes in the generated changeset. See [examples of well written changesets](https://backstage.io/docs/getting-started/contributors#writing-changesets). +5. Add generated changeset to Git +6. Push the commit with your changeset to the branch associated with your PR +7. Accept our gratitude for making the release process easier on the maintainers For more information, checkout [adding a changeset](https://github.com/atlassian/changesets/blob/master/docs/adding-a-changeset.md) documentation in the changesets repository. diff --git a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md index b121bab25f..6b1b8cb4e7 100644 --- a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md +++ b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md @@ -100,17 +100,16 @@ const app = createApp({ ### Backend -When using ALB auth it is not possible to leverage the built-in auth config discovery mechanism implemented in the app created by default; bespoke logic needs to be implemented. +When using ALB auth you can configure it as described [here](https://backstage.io/docs/auth/identity-resolver). -- replace the content of `packages/backend/plugin/auth.ts` with the below +- replace the content of `packages/backend/plugin/auth.ts` with the below and tweak it according to your needs. ```ts import { createRouter, - AuthResponse, - AuthProviderFactoryOptions, - defaultAuthProviderFactories, + createAwsAlbProvider, } from '@backstage/plugin-auth-backend'; +import { Router } from 'express'; import { PluginEnvironment } from '../types'; export default async function createPlugin({ @@ -118,30 +117,59 @@ export default async function createPlugin({ database, config, discovery, -}: PluginEnvironment) { - const identityResolver = (payload: any): Promise> => { - return Promise.resolve({ - providerInfo: {}, - profile: { - email: payload.email, - displayName: payload.name, - picture: payload.picture, - }, - backstageIdentity: { - id: payload.email, - }, - }); - }; - const providerFactories = { - awsalb: (options: AuthProviderFactoryOptions) => - defaultAuthProviderFactories.awsalb({ ...options, identityResolver }), - }; +}: PluginEnvironment): Promise { return await createRouter({ logger, config, database, discovery, - providerFactories, + providerFactories: { + awsalb: createAwsAlbProvider({ + authHandler: async ({ fullProfile }) => { + let email: string | undefined = undefined; + if (fullProfile.emails && fullProfile.emails.length > 0) { + const [firstEmail] = fullProfile.emails; + email = firstEmail.value; + } + + let picture: string | undefined = undefined; + if (fullProfile.photos && fullProfile.photos.length > 0) { + const [firstPhoto] = fullProfile.photos; + picture = firstPhoto.value; + } + + const displayName: string | undefined = + fullProfile.displayName ?? fullProfile.username ?? fullProfile.id; + + return { + profile: { + email, + picture, + displayName, + }, + }; + }, + signIn: { + resolver: async ({ profile: { email } }, ctx) => { + const [id] = email?.split('@') ?? ''; + // Fetch from an external system that returns entity claims like: + // ['user:default/breanna.davison', ...] + const ent = [`user:default/${id}`]; + + // Resolve group membership from the Backstage catalog + const fullEnt = + await ctx.catalogIdentityClient.resolveCatalogMembership({ + entityRefs: [id].concat(ent), + logger: ctx.logger, + }); + const token = await ctx.tokenIssuer.issueToken({ + claims: { sub: id, ent: fullEnt }, + }); + return { id, token }; + }, + }, + }), + }, }); } ``` diff --git a/docs/features/search/how-to-guides.md b/docs/features/search/how-to-guides.md new file mode 100644 index 0000000000..b6ea6ae364 --- /dev/null +++ b/docs/features/search/how-to-guides.md @@ -0,0 +1,46 @@ +--- +id: how-to-guides +title: Search "HOW TO" guides +sidebar_label: "HOW TO" guides +description: Search "HOW TO" guides +--- + +## How to implement your own Search API + +The Search plugin provides implementation of one primary API by default: the +[SearchApi](https://github.com/backstage/backstage/blob/db2666b980853c281b8fe77905d7639c5d255f13/plugins/search/src/apis.ts#L35), +which is responsible for talking to the search-backend to query search results. + +There may be occasions where you need to implement this API yourself, to +customize it to your own needs - for example if you have your own search backend +that you want to talk to. The purpose of this guide is to walk you through how +to do that in two steps. + +1. Implement the `SearchApi` + [interface](https://github.com/backstage/backstage/blob/db2666b980853c281b8fe77905d7639c5d255f13/plugins/search/src/apis.ts#L31) + according to your needs. + +```typescript +export class SearchClient implements SearchApi { + // your implementation +} +``` + +2. Override the API ref `searchApiRef` with your new implemented API in the + `App.tsx` using `ApiFactories`. + [Read more about App APIs](https://backstage.io/docs/api/utility-apis#app-apis). + +```typescript +const app = createApp({ + apis: [ + // SearchApi + createApiFactory({ + api: searchApiRef, + deps: { discovery: discoveryApiRef }, + factory({ discovery }) { + return new SearchClient({ discoveryApi: discovery }); + }, + }), + ], +}); +``` diff --git a/docs/getting-started/contributors.md b/docs/getting-started/contributors.md index e12c0c5f17..ed56d63cab 100644 --- a/docs/getting-started/contributors.md +++ b/docs/getting-started/contributors.md @@ -139,3 +139,92 @@ default app configs. You can learn more about the local configuration in [Static Configuration in Backstage](../conf/) section. + +## Writing changesets + +Changesets are an important part of the development process. They are used to +generate Changelog entries for all changes to the project. Ultimately they are +read by the end users to learn about important changes and fixes to the project. +Some of these fixes might require manual intervention from users so it's +important to write changesets that users understand and can take action on. + +Here are some important do's and don'ts when writing changesets: + +### Changeset should give a clear description to what has changed + +#### Bad + +``` +--- +'@backstage/catalog': patch +--- +Fixed table layout +``` + +#### Good + +``` +--- +'@backstage/catalog': patch +--- + +Fixed bug in EntityTable component where table layout did not readjust properly below 1080x768 pixels. +``` + +### Breaking changes not caught by the type checker should be clearly marked with bold **BREAKING** text + +#### Bad + +``` +--- +'@backstage/catalog': minor +--- + +getEntity is now a function that returns a Promise. +``` + +#### Good + +``` +--- +'@backstage/catalog': minor +--- + +**BREAKING** The getEntity function now returns a Promise and **must** be awaited from now on. +``` + +### Changes to code should include a diff of the files that need updating + +#### Bad + +``` +--- +'@backstage/catalog': patch +--- + +**BREAKING** The catalogEngine now requires a flux capacitor to be passed. +``` + +#### Good + + --- + '@backstage/catalog': patch + --- + + **BREAKING** The catalog createRouter now requires that a `FluxCapacitor` is + passed to the router. + + These changes are **required** to `packages/backend/src/plugins/catalog.ts` + + ```diff + + import { FluxCapacitor } from '@backstage/time'; + + const fluxCapacitor = new FluxCapacitor(); + return await createRouter({ + entitiesCatalog, + locationAnalyzer, + locationService, + + fluxCapacitor, + logger: env.logger, + config: env.config, + }); + ``` diff --git a/docs/integrations/google-analytics/installation.md b/docs/integrations/google-analytics/installation.md deleted file mode 100644 index d6c23cc4b0..0000000000 --- a/docs/integrations/google-analytics/installation.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -id: installation -title: Google Analytics Installation -sidebar_label: Installation -# prettier-ignore -description: Adding Google Analytics to Your App ---- - -There is a basic -[Google Analytics](https://marketingplatform.google.com/about/analytics/) -integration built into Backstage. You can enable it by adding the following to -your app configuration: - -```yaml -app: - googleAnalyticsTrackingId: UA-000000-0 -``` - -Replace the tracking ID with the one generated for you after signing up for the -Google Analytics service. - -The default behavior is only to send a pageview hit. To record more, review the -[Google Analytics developer documentation](https://developers.google.com/analytics/devguides/collection/gtagjs). diff --git a/docs/plugins/analytics.md b/docs/plugins/analytics.md new file mode 100644 index 0000000000..0b3d5dfd9c --- /dev/null +++ b/docs/plugins/analytics.md @@ -0,0 +1,298 @@ +--- +id: analytics +title: Plugin Analytics +description: Measuring usage of your Backstage instance. +--- + +Setting up, maintaining, and iterating on an instance of Backstage can be a +large investment. To help measure return on this investment, Backstage comes +with an event-based Analytics API that grants app integrators the flexibility to +collect and analyze Backstage usage in the analytics tool of their choice, while +providing plugin developers a standard interface for instrumenting key user +interactions. + +## Concepts + +- **Events** consist of, at a minimum, an `action` (like `click`) and a + `subject` (like `thing that was clicked on`). +- **Attributes** represent additional dimensional data (in the form of key/value + pairs) that may be provided on an event-by-event basis. To continue the above + example, the URL a user clicked to might look like `{ "to": "/a/page" }`. +- **Context** represents the broader context in which an event took place. By + default, information like `pluginId`, `extension`, and `routeRef` are + provided. + +This composition of events aims to allow analysis at different levels of detail, +enabling very granular questions (like "what is the most clicked on thing on a +particular route") as well as very high-level questions (like "what is the most +used plugin in my Backstage instance") to be answered. + +## Supported Analytics Tools + +While all that's needed to consume and forward these events to an analytics tool +is a concrete implementation of [AnalyticsApi][analytics-api-type], common +integrations are packaged and provided as plugins. Find your analytics tool of +choice below. + +| Analytics Tool | Support Status | +| ---------------------- | -------------- | +| [Google Analytics][ga] | Yes โœ… | + +To suggest an integration, please [open an issue][add-tool] for the analytics +tool your organization uses. Or jump to [Writing Integrations][int-howto] to +learn how to contribute the integration yourself! + +[ga]: + https://github.com/backstage/backstage/blob/master/plugins/analytics-module-ga/README.md +[add-tool]: + https://github.com/backstage/backstage/issues/new?assignees=&labels=plugin&template=plugin_template.md&title=%5BAnalytics+Module%5D+THE+ANALYTICS+TOOL+TO+INTEGRATE +[int-howto]: #writing-integrations +[analytics-api-type]: + https://backstage.io/docs/reference/core-plugin-api.analyticsapi + +## Key Events + +The following table summarizes events that, depending on the plugins you have +installed, may be captured. + +| Action | Provided By | Subject | +| ---------- | -------------- | ----------------------------------------- | +| `navigate` | Backstage Core | The URL of the page that was navigated to | +| `click` | Backstage Core | The text of the link that was clicked on | + +If there is an event you'd like to see captured, please [open an +issue][add-event] describing the event you want to see and the questions it +would help you answer. Or jump to [Capturing Events][event-howto] to learn how +to contribute the instrumentation yourself! + +_OSS plugin maintainers: feel free to document your events in the table above._ + +[add-event]: +https://github.com/backstage/backstage/issues/new?assignees=&labels=enhancement&template=feature_template.md&title=[Analytics%20Event]:%20THE+EVENT+TO+CAPTURE +[event-howto]: #capturing-events + +## Writing Integrations + +Analytics event forwarding is implemented as a Backstage utility API. Just as +you might provide a custom API implementation for errors or SCM Authentication, +you can provide one for analytics. + +The provided API need only provide a single method `captureEvent`, which takes +an `AnalyticsEvent` object. + +```ts +import { + analyticsApiRef, + AnalyticsEvent, + AnyApiFactory, + createApiFactory, +} from '@backstage/core-plugin-api'; + +export const apis: AnyApiFactory[] = [ + createApiFactory(analyticsApiRef, { + captureEvent: (event: AnalyticsEvent) => { + window._AcmeAnalyticsQ.push(event); + }, + }), +]; +``` + +In reality, you would likely want to encapsulate instantiation logic and pull +some details from configuration. A more complete example might look like: + +```ts +import { + AnalyticsApi, + analyticsApiRef, + AnalyticsEvent, + AnyApiFactory, + configApiRef, + createApiFactory, +} from '@backstage/core-plugin-api'; +import { AcmeAnalytics } from 'acme-analytics'; + +class AcmeAnalytics implements AnalyticsApi { + private constructor(accountId: number) { + AcmeAnalytics.init(accountId); + } + + static fromConfig(config) { + const accountId = config.getString('app.analytics.acme.id'); + return new AcmeAnalytics(accountId); + } + + captureEvent(event: AnalyticsEvent) { + const { action, ...rest } = event; + AcmeAnalytics.send(action, rest); + } +} + +export const apis: AnyApiFactory[] = [ + createApiFactory({ + api: analyticsApiRef, + deps: { configApi: configApiRef }, + factory: ({ configApi }) => AcmeAnalytics.fromConfig(configApi), + }), +]; +``` + +If you are integrating with an analytics service (as opposed to an internal +tool), consider contributing your API implementation as a plugin! + +By convention, such packages should be named +`@backstage/analytics-module-[name]`, and any configuration should be keyed +under `app.analytics.[name]`. + +## Capturing Events + +To instrument an event in a component, start by retrieving an analytics tracker +using the `useAnalytics()` hook provided by `@backstage/core-plugin-api`. The +tracker includes a `captureEvent` method which takes an `action` and a `subject` +as arguments. + +```ts +import { useAnalytics } from '@backstage/core-plugin-api'; + +const analytics = useAnalytics(); +analytics.captureEvent('deploy', serviceName); +``` + +### Providing Extra Attributes + +Additional dimensional `attributes` as well as a numeric `value` can be provided +on a third `options` argument if/when relevant for the event: + +```ts +analytics.captureEvent('merge', pullRequestName, { + value: pullRequestAgeInMinutes, + attributes: { + org, + repo, + }, +}); +``` + +In the above example, an event resembling the following object would be +captured: + +```json +{ + "action": "merge", + "subject": "Name of Pull Request", + "value": 60, + "attributes": { + "org": "some-org", + "repo": "some-repo" + } +} +``` + +### Providing Context for Events + +The `attributes` option is good for capturing details available to you within +the component that you're instrumenting. For capturing metadata only available +further up the react tree, or to help app integrators aggregate distinct events +by some common value, use an ``. + +```tsx +import { AnalyticsContext, useAnalytics } from '@backstage/core-plugin-api'; + +const MyComponent = ({ value }) => { + const analytics = useAnalytics(); + const handleClick = () => analytics.captureEvent('check', value); + return ; +}; + +const MyWrapper = () => { + return ( + + + + ); +}; +``` + +In the above example, clicking on `` would result in an analytics +event resembling: + +```json +{ + "action": "check", + "subject": "Some Value", + "context": { + "segment": "xyz" + } +} +``` + +Note that, for brevity in the example above, the context keys provided by +Backstage core (`pluginId`, `extension`, and `routeRef`) have been omitted. In +reality, those details would be included alongside any additional context +provided by you. + +Analytics contexts can be nested; their values are merged down the react tree, +allowing keys to be overwritten. + +### Event Naming Considerations + +An event is split into its constituent parts to enable analysis at various +levels of granularity. In order to maintain this flexibility at analysis-time, +it's important to keep each of these levels of detail disaggregated. + +- Avoid providing an overly specific `action`. For example, instead of + `filterEntityTable`, consider just using `filter` as the action, and allowing + `EntityTable` to be specified as part of the event's `context` (most likely + automatically as part of the `extension` in which the `filter` event was + captured). + +- On the flip side, when adding `attributes` to an event, look at existing + events and see if the data you are capturing matches the intention, type, or + even the content of _their_ `attributes`. For instance, it may be common for + events that involve the Catalog to add details like entity `name`, `kind`, + and/or `namespace` as `attributes`. Using the same keys in your event will + ensure that events instrumented across plugins can easily be aggregated. + +### Unit Testing Event Capture + +The `@backstage/test-utils` package includes a `MockAnalyticsApi` implementation +that you can use in your unit tests to spy on and make assertions about any +analytics events captured. + +Use it like this: + +```tsx +import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; +import { analyticsApiRef } from '@backstage/core-plugin-api'; +import { MockAnalyticsApi, wrapInTestApp } from '@backstage/test-utils'; +import { render, fireEvent, waitFor } from '@testing-library/react'; + +describe('SomeComponent', () => { + it('should capture event on click', () => { + // Use the Mock Analytics API to spy on event captures. + const apiSpy = new MockAnalyticsApi(); + + // Render the component being tested + const { getByText } = render( + wrapInTestApp( + + + , + ), + ); + + // Fire the event that triggers event capture. + fireEvent.click(getByText('some component text')); + + // Assert that the event was captured with the expected data. + await waitFor(() => { + expect(apiSpy.getEvents()[0]).toMatchObject({ + action: 'expected action', + subject: 'expected subject'', + attributes: { + foo: 'bar', + }, + }); + }); + }); +}); +``` diff --git a/docs/plugins/github-apps.md b/docs/plugins/github-apps.md index ef0b21d902..1be1502b6b 100644 --- a/docs/plugins/github-apps.md +++ b/docs/plugins/github-apps.md @@ -60,7 +60,7 @@ The YAML file must include the following information. Please note that the indentation for the `privateKey` is required. ```yaml -appId: 1 +appId: app id clientId: client id clientSecret: client secret webhookSecret: webhook secret @@ -95,7 +95,7 @@ If you want to limit the GitHub app installations visible to backstage you may optionally include the `allowedInstallationOwners` option. ```yaml -appId: 1 +appId: app id allowedInstallationOwners: ['GlobexCorp'] clientId: client id clientSecret: client secret diff --git a/docs/plugins/observability.md b/docs/plugins/observability.md index b5a6a0ba60..d8778f07f8 100644 --- a/docs/plugins/observability.md +++ b/docs/plugins/observability.md @@ -8,11 +8,6 @@ description: Adding Observability to Your Plugin This article briefly describes the observability options that are available to a Backstage integrator. -## Google Analytics - -See how to install Google Analytics in your app -[here](../integrations/google-analytics/installation.md) - ## Datadog RUM Events See how to install Datadog Events in your app diff --git a/microsite/data/plugins/backstage-analytics-module-ga.yaml b/microsite/data/plugins/backstage-analytics-module-ga.yaml new file mode 100644 index 0000000000..0a03ec20e8 --- /dev/null +++ b/microsite/data/plugins/backstage-analytics-module-ga.yaml @@ -0,0 +1,9 @@ +--- +title: 'Analytics Module: Google Analytics' +author: Spotify +authorUrl: https://github.com/spotify +category: Analytics +description: Track usage of your Backstage instance using Google Analytics. +documentation: https://github.com/backstage/backstage/blob/master/plugins/analytics-module-ga/README.md +iconUrl: img/ga-icon.png +npmPackageName: '@backstage/plugin-analytics-module-ga' diff --git a/microsite/data/plugins/grafana.yaml b/microsite/data/plugins/grafana.yaml new file mode 100644 index 0000000000..ff4e33e3df --- /dev/null +++ b/microsite/data/plugins/grafana.yaml @@ -0,0 +1,13 @@ +--- +title: Grafana +author: K-Phoen +authorUrl: https://github.com/K-Phoen +category: Monitoring +description: Associate alerts and dashboards to components. +documentation: https://github.com/K-Phoen/backstage-plugin-grafana/ +iconUrl: https://avatars.githubusercontent.com/u/7195757?s=200&v=4 +npmPackageName: '@k-phoen/backstage-plugin-grafana' +tags: + - dashboards + - monitoring + - alerting diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 8b8b359fdc..c269f8ac8f 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -90,7 +90,8 @@ "features/search/getting-started", "features/search/concepts", "features/search/architecture", - "features/search/search-engines" + "features/search/search-engines", + "features/search/how-to-guides" ] }, { @@ -153,11 +154,6 @@ "label": "GitLab", "ids": ["integrations/gitlab/locations"] }, - { - "type": "subcategory", - "label": "Google Analytics", - "ids": ["integrations/google-analytics/installation"] - }, { "type": "subcategory", "label": "Google GCS", @@ -177,6 +173,7 @@ "plugins/structure-of-a-plugin", "plugins/integrating-plugin-into-software-catalog", "plugins/composability", + "plugins/analytics", { "type": "subcategory", "label": "Backends and APIs", diff --git a/microsite/static/css/custom.css b/microsite/static/css/custom.css index ac432372b1..139fa7b1d2 100644 --- a/microsite/static/css/custom.css +++ b/microsite/static/css/custom.css @@ -1217,3 +1217,7 @@ code { .medium-zoom-image { z-index: 10000; } + +h3.collapsible span.arrow { + margin-right: 4px; +} diff --git a/microsite/static/img/ga-icon.png b/microsite/static/img/ga-icon.png new file mode 100644 index 0000000000..8eaebd06aa Binary files /dev/null and b/microsite/static/img/ga-icon.png differ diff --git a/mkdocs.yml b/mkdocs.yml index b668ffdb15..2f4aa4203f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -68,6 +68,7 @@ nav: - Concepts: 'features/search/concepts.md' - Search Architecture: 'features/search/architecture.md' - Search Engines: 'features/search/search-engines.md' + - HOW TO guides: 'features/search/how-to-guides.md' - TechDocs: - Overview: 'features/techdocs/README.md' - Getting Started: 'features/techdocs/getting-started.md' @@ -99,8 +100,6 @@ nav: - Org Data: 'integrations/github/org.md' - GitLab: - Locations: 'integrations/gitlab/locations.md' - - Google Analytics: - - Installation: 'integrations/google-analytics/installation.md' - Google GCS: - Locations: 'integrations/google-cloud-storage/locations.md' - LDAP: @@ -114,6 +113,7 @@ nav: - Plugin Development: 'plugins/plugin-development.md' - Integrate into the Software Catalog: 'plugins/integrating-plugin-into-software-catalog.md' - Composability System: 'plugins/composability.md' + - Plugin Analytics: 'plugins/analytics.md' - Backends and APIs: - Proxying: 'plugins/proxying.md' - Backend plugin: 'plugins/backend-plugin.md' diff --git a/package.json b/package.json index 03a502fee2..9c0f41ca53 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,6 @@ "devDependencies": { "@types/webpack": "^5.28.0", "@changesets/cli": "^2.14.0", - "@octokit/openapi-types": "^2.2.0", "@spotify/prettier-config": "^11.0.0", "command-exists": "^1.2.9", "concurrently": "^6.0.0", @@ -71,7 +70,6 @@ "lerna": "^4.0.0", "lint-staged": "^11.1.2", "prettier": "^2.2.1", - "recursive-readdir": "^2.2.2", "shx": "^0.3.2", "yarn-lock-check": "^1.0.5" }, diff --git a/packages/app/src/components/home/HomePage.tsx b/packages/app/src/components/home/HomePage.tsx index ebb486790d..33ac1a7e8c 100644 --- a/packages/app/src/components/home/HomePage.tsx +++ b/packages/app/src/components/home/HomePage.tsx @@ -14,25 +14,26 @@ * limitations under the License. */ -import React from 'react'; -import Grid from '@material-ui/core/Grid'; -import { - HomePageRandomJoke, - ComponentAccordion, - ComponentTabs, - ComponentTab, -} from '@backstage/plugin-home'; import { Content, Header, - Page, HomepageTimer, + Page, } from '@backstage/core-components'; +import { + ComponentAccordion, + ComponentTab, + ComponentTabs, + HomePageRandomJoke, + WelcomeTitle, +} from '@backstage/plugin-home'; import { HomePageSearchBar } from '@backstage/plugin-search'; +import Grid from '@material-ui/core/Grid'; +import React from 'react'; export const HomePage = () => ( -
+
} pageTitleOverride="Home">
diff --git a/packages/backend-common/src/reading/AwsS3UrlReader.test.ts b/packages/backend-common/src/reading/AwsS3UrlReader.test.ts index 80f59fe182..0bc0e4f97f 100644 --- a/packages/backend-common/src/reading/AwsS3UrlReader.test.ts +++ b/packages/backend-common/src/reading/AwsS3UrlReader.test.ts @@ -135,13 +135,7 @@ describe('AwsS3UrlReader', () => { 'getObject', Buffer.from( require('fs').readFileSync( - path.resolve( - 'src', - 'reading', - '__fixtures__', - 'awsS3', - 'awsS3-mock-object.yaml', - ), + path.resolve(__dirname, '__fixtures__/awsS3/awsS3-mock-object.yaml'), ), ), ); @@ -187,13 +181,7 @@ describe('AwsS3UrlReader', () => { 'getObject', Buffer.from( require('fs').readFileSync( - path.resolve( - 'src', - 'reading', - '__fixtures__', - 'awsS3', - 'awsS3-mock-object.yaml', - ), + path.resolve(__dirname, '__fixtures__/awsS3/awsS3-mock-object.yaml'), ), ), ); @@ -249,13 +237,7 @@ describe('AwsS3UrlReader', () => { 'getObject', Buffer.from( require('fs').readFileSync( - path.resolve( - 'src', - 'reading', - '__fixtures__', - 'awsS3', - 'awsS3-mock-object.yaml', - ), + path.resolve(__dirname, '__fixtures__/awsS3/awsS3-mock-object.yaml'), ), ), ); diff --git a/packages/backend-common/src/reading/AzureUrlReader.test.ts b/packages/backend-common/src/reading/AzureUrlReader.test.ts index e014fa2e75..59968a18e9 100644 --- a/packages/backend-common/src/reading/AzureUrlReader.test.ts +++ b/packages/backend-common/src/reading/AzureUrlReader.test.ts @@ -152,7 +152,7 @@ describe('AzureUrlReader', () => { describe('readTree', () => { const repoBuffer = fs.readFileSync( - path.resolve('src', 'reading', '__fixtures__', 'mock-main.zip'), + path.resolve(__dirname, '__fixtures__/mock-main.zip'), ); const processor = new AzureUrlReader( @@ -264,7 +264,7 @@ describe('AzureUrlReader', () => { describe('search', () => { const repoBuffer = fs.readFileSync( - path.resolve('src', 'reading', '__fixtures__', 'mock-main.zip'), + path.resolve(__dirname, '__fixtures__/mock-main.zip'), ); const processor = new AzureUrlReader( diff --git a/packages/backend-common/src/reading/BitbucketUrlReader.test.ts b/packages/backend-common/src/reading/BitbucketUrlReader.test.ts index ef61452163..5bbf3725ea 100644 --- a/packages/backend-common/src/reading/BitbucketUrlReader.test.ts +++ b/packages/backend-common/src/reading/BitbucketUrlReader.test.ts @@ -104,20 +104,13 @@ describe('BitbucketUrlReader', () => { describe('readTree', () => { const repoBuffer = fs.readFileSync( path.resolve( - 'src', - 'reading', - '__fixtures__', - 'bitbucket-repo-with-commit-hash.tar.gz', + __dirname, + '__fixtures__/bitbucket-repo-with-commit-hash.tar.gz', ), ); const privateBitbucketRepoBuffer = fs.readFileSync( - path.resolve( - 'src', - 'reading', - '__fixtures__', - 'bitbucket-server-repo.tar.gz', - ), + path.resolve(__dirname, '__fixtures__/bitbucket-server-repo.tar.gz'), ); beforeEach(() => { @@ -298,10 +291,8 @@ describe('BitbucketUrlReader', () => { describe('search hosted', () => { const repoBuffer = fs.readFileSync( path.resolve( - 'src', - 'reading', - '__fixtures__', - 'bitbucket-repo-with-commit-hash.tar.gz', + __dirname, + '__fixtures__/bitbucket-repo-with-commit-hash.tar.gz', ), ); @@ -386,12 +377,7 @@ describe('BitbucketUrlReader', () => { describe('search private', () => { const privateBitbucketRepoBuffer = fs.readFileSync( - path.resolve( - 'src', - 'reading', - '__fixtures__', - 'bitbucket-server-repo.tar.gz', - ), + path.resolve(__dirname, '__fixtures__/bitbucket-server-repo.tar.gz'), ); beforeEach(() => { diff --git a/packages/backend-common/src/reading/GithubUrlReader.test.ts b/packages/backend-common/src/reading/GithubUrlReader.test.ts index f9717a862a..e70444cf2c 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.test.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.test.ts @@ -254,12 +254,7 @@ describe('GithubUrlReader', () => { describe('readTree', () => { const repoBuffer = fs.readFileSync( - path.resolve( - 'src', - 'reading', - '__fixtures__', - 'backstage-mock-etag123.tar.gz', - ), + path.resolve(__dirname, '__fixtures__/backstage-mock-etag123.tar.gz'), ); const reposGithubApiResponse = { @@ -540,12 +535,7 @@ describe('GithubUrlReader', () => { describe('search', () => { const repoBuffer = fs.readFileSync( - path.resolve( - 'src', - 'reading', - '__fixtures__', - 'backstage-mock-etag123.tar.gz', - ), + path.resolve(__dirname, '__fixtures__/backstage-mock-etag123.tar.gz'), ); const githubTreeContents: GhTreeResponse['tree'] = [ diff --git a/packages/backend-common/src/reading/GitlabUrlReader.test.ts b/packages/backend-common/src/reading/GitlabUrlReader.test.ts index 51b211e698..331150bde3 100644 --- a/packages/backend-common/src/reading/GitlabUrlReader.test.ts +++ b/packages/backend-common/src/reading/GitlabUrlReader.test.ts @@ -223,7 +223,7 @@ describe('GitlabUrlReader', () => { describe('readTree', () => { const archiveBuffer = fs.readFileSync( - path.resolve('src', 'reading', '__fixtures__', 'gitlab-archive.tar.gz'), + path.resolve(__dirname, '__fixtures__/gitlab-archive.tar.gz'), ); const projectGitlabApiResponse = { @@ -495,7 +495,7 @@ describe('GitlabUrlReader', () => { describe('search', () => { const archiveBuffer = fs.readFileSync( - path.resolve('src', 'reading', '__fixtures__', 'gitlab-archive.tar.gz'), + path.resolve(__dirname, '__fixtures__/gitlab-archive.tar.gz'), ); const projectGitlabApiResponse = { diff --git a/packages/cli-common/src/paths.test.ts b/packages/cli-common/src/paths.test.ts index 894060f2e8..5c1cf8af08 100644 --- a/packages/cli-common/src/paths.test.ts +++ b/packages/cli-common/src/paths.test.ts @@ -46,6 +46,8 @@ describe('paths', () => { const dir = resolvePath(__dirname, '..'); const root = resolvePath(__dirname, '../../..'); + jest.spyOn(process, 'cwd').mockReturnValue(dir); + const paths = findPaths(__dirname); expect(paths.ownDir).toBe(dir); diff --git a/packages/cli/config/eslint.backend.js b/packages/cli/config/eslint.backend.js index 99767f5d79..a97f26f8da 100644 --- a/packages/cli/config/eslint.backend.js +++ b/packages/cli/config/eslint.backend.js @@ -14,6 +14,15 @@ * limitations under the License. */ +const globalRestrictedSyntax = [ + { + message: + 'Default import from winston is not allowed, import `* as winston` instead.', + selector: + 'ImportDeclaration[source.value="winston"] ImportDefaultSpecifier', + }, +]; + module.exports = { extends: [ '@spotify/eslint-config-base', @@ -69,17 +78,12 @@ module.exports = { // Avoid default import from winston as it breaks at runtime 'no-restricted-syntax': [ 'error', - { - message: - 'Default import from winston is not allowed, import `* as winston` instead.', - selector: - 'ImportDeclaration[source.value="winston"] ImportDefaultSpecifier', - }, { message: "`__dirname` doesn't refer to the same dir in production builds, try `resolvePackagePath()` from `@backstage/backend-common` instead.", selector: 'Identifier[name="__dirname"]', }, + ...globalRestrictedSyntax, ], }, overrides: [ @@ -103,6 +107,7 @@ module.exports = { bundledDependencies: true, }, ], + 'no-restricted-syntax': ['error', ...globalRestrictedSyntax], }, }, ], diff --git a/packages/cli/config/jest.js b/packages/cli/config/jest.js index 80b3e84cda..c687616a7d 100644 --- a/packages/cli/config/jest.js +++ b/packages/cli/config/jest.js @@ -18,7 +18,7 @@ const fs = require('fs-extra'); const path = require('path'); const glob = require('util').promisify(require('glob')); -async function getProjectConfig(targetPath) { +async function getProjectConfig(targetPath, displayName) { const configJsPath = path.resolve(targetPath, 'jest.config.js'); const configTsPath = path.resolve(targetPath, 'jest.config.ts'); // If the package has it's own jest config, we use that instead. @@ -73,6 +73,7 @@ async function getProjectConfig(targetPath) { const transformModulePattern = transformModules && `(?!${transformModules})`; const options = { + displayName, rootDir: path.resolve(targetPath, 'src'), coverageDirectory: path.resolve(targetPath, 'coverage'), collectCoverageFrom: ['**/*.{js,jsx,ts,tsx}', '!**/*.d.ts'], @@ -143,7 +144,7 @@ async function getRootConfig() { const packageData = await fs.readJson(packagePath); const testScript = packageData.scripts && packageData.scripts.test; if (testScript && testScript.includes('backstage-cli test')) { - return await getProjectConfig(projectPath); + return await getProjectConfig(projectPath, packageData.name); } return undefined; diff --git a/packages/core-app-api/src/app/AppThemeProvider.tsx b/packages/core-app-api/src/app/AppThemeProvider.tsx index 4e283a1d00..4e3a8487a6 100644 --- a/packages/core-app-api/src/app/AppThemeProvider.tsx +++ b/packages/core-app-api/src/app/AppThemeProvider.tsx @@ -15,7 +15,8 @@ */ import React, { useMemo, useEffect, useState, PropsWithChildren } from 'react'; -import { ThemeProvider, CssBaseline } from '@material-ui/core'; +import { ThemeProvider } from '@material-ui/core/styles'; +import CssBaseline from '@material-ui/core/CssBaseline'; import { useApi, appThemeApiRef, AppTheme } from '@backstage/core-plugin-api'; import { useObservable } from 'react-use'; diff --git a/packages/core-components/.eslintrc.js b/packages/core-components/.eslintrc.js index d592a653c8..65a93d3660 100644 --- a/packages/core-components/.eslintrc.js +++ b/packages/core-components/.eslintrc.js @@ -1,8 +1,25 @@ +const base = require('@backstage/cli/config/eslint'); +const [, baseRestrictedImports] = base.rules['no-restricted-imports']; + module.exports = { extends: [require.resolve('@backstage/cli/config/eslint')], rules: { // TODO: add prop types to JS and remove 'react/prop-types': 0, 'jest/expect-expect': 0, + 'no-restricted-imports': [ + 2, + { + ...baseRestrictedImports, + paths: [ + { + // Importing the entire MUI icons packages kills build performance as the list of icons is huge. + name: '@material-ui/core', + message: "Please import '@material-ui/core/...' instead.", + }, + ...baseRestrictedImports.paths, + ], + }, + ], }, }; diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 4a94135b4a..38fe19fc8b 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -8,22 +8,21 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstageIdentityApi } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; -import { Breadcrumbs as Breadcrumbs_2 } from '@material-ui/core'; -import { ButtonProps as ButtonProps_2 } from '@material-ui/core'; -import { CardHeaderProps } from '@material-ui/core'; +import { ButtonProps as ButtonProps_2 } from '@material-ui/core/Button'; +import { CardHeaderProps } from '@material-ui/core/CardHeader'; import { Column } from '@material-table/core'; import { ComponentClass } from 'react'; import { ComponentProps } from 'react'; import { Context } from 'react'; import { default as CSS_2 } from 'csstype'; import { CSSProperties } from 'react'; -import { default as dagre_2 } from 'dagre'; import { ElementType } from 'react'; import { ErrorInfo } from 'react'; import { IconComponent } from '@backstage/core-plugin-api'; -import { LinearProgressProps } from '@material-ui/core'; -import { LinkProps as LinkProps_2 } from '@material-ui/core'; +import { LinearProgressProps } from '@material-ui/core/LinearProgress'; +import { LinkProps as LinkProps_2 } from '@material-ui/core/Link'; import { LinkProps as LinkProps_3 } from 'react-router-dom'; +import MaterialBreadcrumbs from '@material-ui/core/Breadcrumbs'; import { MaterialTableProps } from '@material-table/core'; import { NavLinkProps } from 'react-router-dom'; import { Overrides } from '@material-ui/core/styles/overrides'; @@ -37,14 +36,14 @@ import { SessionApi } from '@backstage/core-plugin-api'; import { SignInPageProps } from '@backstage/core-plugin-api'; import { SparklinesLineProps } from 'react-sparklines'; import { SparklinesProps } from 'react-sparklines'; -import { StyledComponentProps } from '@material-ui/core'; +import { StyledComponentProps } from '@material-ui/core/styles'; import { StyleRules } from '@material-ui/styles'; import { StyleRules as StyleRules_2 } from '@material-ui/core/styles/withStyles'; -import { TabProps } from '@material-ui/core'; +import { TabProps } from '@material-ui/core/Tab'; import { TextTruncateProps } from 'react-text-truncate'; -import { Theme } from '@material-ui/core'; -import { TooltipProps } from '@material-ui/core'; -import { WithStyles } from '@material-ui/core'; +import { Theme } from '@material-ui/core/styles'; +import { TooltipProps } from '@material-ui/core/Tooltip'; +import { WithStyles } from '@material-ui/core/styles'; // Warning: (ae-missing-release-tag) "AlertDisplay" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -53,15 +52,11 @@ export function AlertDisplay(_props: {}): JSX.Element | null; // Warning: (ae-missing-release-tag) "Alignment" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public enum Alignment { - // (undocumented) DOWN_LEFT = 'DL', - // (undocumented) DOWN_RIGHT = 'DR', - // (undocumented) UP_LEFT = 'UL', - // (undocumented) UP_RIGHT = 'UR', } @@ -264,11 +259,10 @@ export type CustomProviderClassKey = 'form' | 'button'; // @public (undocumented) export function DashboardIcon(props: IconComponentProps): JSX.Element; -// Warning: (ae-forgotten-export) The symbol "CustomType" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "DependencyEdge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -type DependencyEdge = T & { +// @public +type DependencyEdge = T & { from: string; to: string; label?: string; @@ -276,8 +270,10 @@ type DependencyEdge = T & { // Warning: (ae-missing-release-tag) "DependencyGraph" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export function DependencyGraph(props: DependencyGraphProps): JSX.Element; +// @public +export function DependencyGraph( + props: DependencyGraphProps, +): JSX.Element; // Warning: (ae-missing-release-tag) "DependencyGraphDefaultLabelClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -301,40 +297,42 @@ export type DependencyGraphNodeClassKey = 'node'; // Warning: (ae-missing-release-tag) "DependencyGraphProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export type DependencyGraphProps = React_2.SVGProps & { - edges: DependencyEdge[]; - nodes: DependencyNode[]; - direction?: Direction; +// @public +export interface DependencyGraphProps + extends React_2.SVGProps { + acyclicer?: 'greedy'; + // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver align?: Alignment; - nodeMargin?: number; + defs?: SVGDefsElement | SVGDefsElement[]; + // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver + direction?: Direction; edgeMargin?: number; - rankMargin?: number; + edgeRanks?: number; + edges: DependencyEdge[]; + edgeWeight?: number; + labelOffset?: number; + // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver + labelPosition?: LabelPosition; + nodeMargin?: number; + nodes: DependencyNode[]; paddingX?: number; paddingY?: number; - acyclicer?: 'greedy'; + // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver ranker?: Ranker; - labelPosition?: LabelPosition; - labelOffset?: number; - edgeRanks?: number; - edgeWeight?: number; - renderNode?: RenderNodeFunction; - renderLabel?: RenderLabelFunction; - defs?: SVGDefsElement | SVGDefsElement[]; + rankMargin?: number; + renderLabel?: RenderLabelFunction; + renderNode?: RenderNodeFunction; zoom?: 'enabled' | 'disabled' | 'enable-on-click'; -}; +} declare namespace DependencyGraphTypes { export { DependencyEdge, - GraphEdge, RenderLabelProps, RenderLabelFunction, DependencyNode, - GraphNode, RenderNodeProps, RenderNodeFunction, - EdgeProperties, Direction, Alignment, Ranker, @@ -345,22 +343,18 @@ export { DependencyGraphTypes }; // Warning: (ae-missing-release-tag) "DependencyNode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -type DependencyNode = T & { +// @public +type DependencyNode = T & { id: string; }; // Warning: (ae-missing-release-tag) "Direction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public enum Direction { - // (undocumented) BOTTOM_TOP = 'BT', - // (undocumented) LEFT_RIGHT = 'LR', - // (undocumented) RIGHT_LEFT = 'RL', - // (undocumented) TOP_BOTTOM = 'TB', } @@ -387,20 +381,6 @@ export type DismissbleBannerClassKey = // @public (undocumented) export function DocsIcon(props: IconComponentProps): JSX.Element; -// Warning: (ae-missing-release-tag) "EdgeProperties" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -type EdgeProperties = { - label?: string; - width?: number; - height?: number; - labeloffset?: number; - labelpos?: LabelPosition; - minlen?: number; - weight?: number; - [customKey: string]: any; -}; - // Warning: (ae-missing-release-tag) "EmailIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -520,18 +500,6 @@ export type GaugeClassKey = 'root' | 'overlay' | 'circle' | 'colorUnknown'; // @public (undocumented) export function GitHubIcon(props: IconComponentProps): JSX.Element; -// Warning: (ae-missing-release-tag) "GraphEdge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -type GraphEdge = DependencyEdge & - dagre_2.GraphEdge & - EdgeProperties; - -// Warning: (ae-missing-release-tag) "GraphNode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -type GraphNode = dagre_2.Node>; - // Warning: (ae-missing-release-tag) "GroupIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -738,7 +706,7 @@ export type ItemCardHeaderProps = Partial> & { // Warning: (ae-missing-release-tag) "LabelPosition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public enum LabelPosition { // (undocumented) CENTER = 'c', @@ -888,37 +856,43 @@ export function Progress( // Warning: (ae-missing-release-tag) "Ranker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public enum Ranker { - // (undocumented) LONGEST_PATH = 'longest-path', - // (undocumented) NETWORK_SIMPLEX = 'network-simplex', - // (undocumented) TIGHT_TREE = 'tight-tree', } // Warning: (ae-missing-release-tag) "RenderLabelFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -type RenderLabelFunction = (props: RenderLabelProps) => React.ReactNode; +// @public +type RenderLabelFunction = ( + props: RenderLabelProps, +) => React_2.ReactNode; // Warning: (ae-missing-release-tag) "RenderLabelProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver +// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver // -// @public (undocumented) -type RenderLabelProps = { +// @public +type RenderLabelProps = { edge: DependencyEdge; }; // Warning: (ae-missing-release-tag) "RenderNodeFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver // -// @public (undocumented) -type RenderNodeFunction = (props: RenderNodeProps) => React.ReactNode; +// @public +type RenderNodeFunction = ( + props: RenderNodeProps, +) => React_2.ReactNode; // Warning: (ae-missing-release-tag) "RenderNodeProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver +// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver // -// @public (undocumented) -type RenderNodeProps = { +// @public +type RenderNodeProps = { node: DependencyNode; }; @@ -2498,6 +2472,8 @@ export type WarningPanelClassKey = // Warnings were encountered during analysis: // +// src/components/DependencyGraph/types.d.ts:14:5 - (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/core-components" does not have an export "DependencyNode" +// src/components/DependencyGraph/types.d.ts:18:5 - (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/core-components" does not have an export "DependencyNode" // src/components/TabbedLayout/RoutedTabs.d.ts:9:5 - (ae-forgotten-export) The symbol "SubRoute" needs to be exported by the entry point index.d.ts // src/components/Table/Table.d.ts:19:5 - (ae-forgotten-export) The symbol "SelectedFilters" needs to be exported by the entry point index.d.ts // src/layout/ErrorBoundary/ErrorBoundary.d.ts:7:5 - (ae-forgotten-export) The symbol "SlackChannel" needs to be exported by the entry point index.d.ts diff --git a/packages/core-components/package.json b/packages/core-components/package.json index fb3d564061..eaad5e8a95 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -37,7 +37,6 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", - "@types/dagre": "^0.7.44", "@types/react": "*", "@types/react-sparklines": "^1.7.0", "@types/react-text-truncate": "^0.14.0", @@ -79,6 +78,7 @@ "@types/d3-selection": "^2.0.0", "@types/d3-shape": "^3.0.1", "@types/d3-zoom": "^2.0.0", + "@types/dagre": "^0.7.44", "@types/google-protobuf": "^3.7.2", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", diff --git a/packages/core-components/src/components/AlertDisplay/AlertDisplay.tsx b/packages/core-components/src/components/AlertDisplay/AlertDisplay.tsx index f43dbd08dd..3a7ec392ee 100644 --- a/packages/core-components/src/components/AlertDisplay/AlertDisplay.tsx +++ b/packages/core-components/src/components/AlertDisplay/AlertDisplay.tsx @@ -15,7 +15,8 @@ */ import React, { useEffect, useState } from 'react'; -import { Snackbar, IconButton } from '@material-ui/core'; +import Snackbar from '@material-ui/core/Snackbar'; +import IconButton from '@material-ui/core/IconButton'; import CloseIcon from '@material-ui/icons/Close'; import { Alert } from '@material-ui/lab'; import { AlertMessage, useApi, alertApiRef } from '@backstage/core-plugin-api'; diff --git a/packages/core-components/src/components/Avatar/Avatar.tsx b/packages/core-components/src/components/Avatar/Avatar.tsx index 0156c58b92..32ce92b46b 100644 --- a/packages/core-components/src/components/Avatar/Avatar.tsx +++ b/packages/core-components/src/components/Avatar/Avatar.tsx @@ -14,12 +14,8 @@ * limitations under the License. */ import React, { CSSProperties } from 'react'; -import { - Avatar as MaterialAvatar, - createStyles, - makeStyles, - Theme, -} from '@material-ui/core'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import MaterialAvatar from '@material-ui/core/Avatar'; import { extractInitials, stringToColor } from './utils'; export type AvatarClassKey = 'avatar'; diff --git a/packages/core-components/src/components/Button/Button.stories.tsx b/packages/core-components/src/components/Button/Button.stories.tsx index 3451a77765..32c3a98bcc 100644 --- a/packages/core-components/src/components/Button/Button.stories.tsx +++ b/packages/core-components/src/components/Button/Button.stories.tsx @@ -17,15 +17,13 @@ import React, { ComponentType } from 'react'; import { Button } from './Button'; import { useLocation } from 'react-router-dom'; import { createRouteRef, useRouteRef } from '@backstage/core-plugin-api'; -import { - Divider, - Link, - List, - ListItem, - ListItemText, - Typography, - Button as MaterialButton, -} from '@material-ui/core'; +import Divider from '@material-ui/core/Divider'; +import Link from '@material-ui/core/Link'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; +import Typography from '@material-ui/core/Typography'; +import MaterialButton from '@material-ui/core/Button'; import { wrapInTestApp } from '@backstage/test-utils'; const routeRef = createRouteRef({ diff --git a/packages/core-components/src/components/Button/Button.tsx b/packages/core-components/src/components/Button/Button.tsx index bfbd6d7a3c..d5c59c3169 100644 --- a/packages/core-components/src/components/Button/Button.tsx +++ b/packages/core-components/src/components/Button/Button.tsx @@ -14,10 +14,9 @@ * limitations under the License. */ -import { - Button as MaterialButton, +import MaterialButton, { ButtonProps as MaterialButtonProps, -} from '@material-ui/core'; +} from '@material-ui/core/Button'; import React from 'react'; import { Link, LinkProps } from '../Link'; diff --git a/packages/core-components/src/components/Chip/Chip.stories.tsx b/packages/core-components/src/components/Chip/Chip.stories.tsx index ff52a81c16..f5cdfd3f16 100644 --- a/packages/core-components/src/components/Chip/Chip.stories.tsx +++ b/packages/core-components/src/components/Chip/Chip.stories.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { Chip } from '@material-ui/core'; +import Chip from '@material-ui/core/Chip'; export default { title: 'Data Display/Chip', diff --git a/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx b/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx index 3ce041a819..3aa2f42984 100644 --- a/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx +++ b/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx @@ -14,12 +14,56 @@ * limitations under the License. */ -import React from 'react'; -import SyntaxHighlighter from 'react-syntax-highlighter'; -import { docco, dark } from 'react-syntax-highlighter/dist/cjs/styles/hljs'; -import { useTheme } from '@material-ui/core'; +import React, { lazy, Suspense } from 'react'; +import { useTheme } from '@material-ui/core/styles'; import { BackstageTheme } from '@backstage/theme'; import { CopyTextButton } from '../CopyTextButton'; +import { Progress } from '../Progress'; + +const LazySyntaxHighlighter = lazy(async () => { + const [{ default: SyntaxHighlighter }, { docco, dark }] = await Promise.all([ + import('react-syntax-highlighter'), + import('react-syntax-highlighter/dist/cjs/styles/hljs'), + ]); + + function LazyHighlighter(props: CodeSnippetProps) { + const { + text, + language, + showLineNumbers = false, + highlightedNumbers, + customStyle, + } = props; + const theme = useTheme(); + const mode = theme.palette.type === 'dark' ? dark : docco; + const highlightColor = + theme.palette.type === 'dark' ? '#256bf3' : '#e6ffed'; + + return ( + + highlightedNumbers?.includes(lineNumber) + ? { + style: { + backgroundColor: highlightColor, + }, + } + : {} + } + > + {text} + + ); + } + + return { default: LazyHighlighter }; +}); /** * Properties for {@link CodeSnippet} @@ -68,38 +112,12 @@ export interface CodeSnippetProps { * providing consistent theming and copy code button */ export function CodeSnippet(props: CodeSnippetProps) { - const { - text, - language, - showLineNumbers = false, - showCopyCodeButton = false, - highlightedNumbers, - customStyle, - } = props; - const theme = useTheme(); - const mode = theme.palette.type === 'dark' ? dark : docco; - const highlightColor = theme.palette.type === 'dark' ? '#256bf3' : '#e6ffed'; + const { text, showCopyCodeButton = false } = props; return (
- - highlightedNumbers?.includes(lineNumber) - ? { - style: { - backgroundColor: highlightColor, - }, - } - : {} - } - > - {text} - + }> + + {showCopyCodeButton && (
diff --git a/packages/core-components/src/components/CopyTextButton/CopyTextButton.tsx b/packages/core-components/src/components/CopyTextButton/CopyTextButton.tsx index 2d2fb4006a..3986b8fa43 100644 --- a/packages/core-components/src/components/CopyTextButton/CopyTextButton.tsx +++ b/packages/core-components/src/components/CopyTextButton/CopyTextButton.tsx @@ -15,7 +15,8 @@ */ import { errorApiRef, useApi } from '@backstage/core-plugin-api'; -import { IconButton, Tooltip } from '@material-ui/core'; +import IconButton from '@material-ui/core/IconButton'; +import Tooltip from '@material-ui/core/Tooltip'; import CopyIcon from '@material-ui/icons/FileCopy'; import React, { MouseEventHandler, useEffect, useState } from 'react'; import { useCopyToClipboard } from 'react-use'; diff --git a/packages/core-components/src/components/CreateButton/CreateButton.tsx b/packages/core-components/src/components/CreateButton/CreateButton.tsx index c6b9b7abed..074ee9fc9f 100644 --- a/packages/core-components/src/components/CreateButton/CreateButton.tsx +++ b/packages/core-components/src/components/CreateButton/CreateButton.tsx @@ -15,7 +15,9 @@ */ import { BackstageTheme } from '@backstage/theme'; -import { Button, IconButton, useMediaQuery } from '@material-ui/core'; +import Button from '@material-ui/core/Button'; +import IconButton from '@material-ui/core/IconButton'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; import React from 'react'; import { Link as RouterLink, LinkProps } from 'react-router-dom'; import AddCircleOutline from '@material-ui/icons/AddCircleOutline'; diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index abc98dd60a..1c3a4fce96 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -29,39 +29,148 @@ import { Ranker, RenderNodeFunction, RenderLabelFunction, - GraphEdge, - GraphNode, LabelPosition, } from './types'; import { Node } from './Node'; -import { Edge } from './Edge'; +import { Edge, GraphEdge } from './Edge'; import { ARROW_MARKER_ID } from './constants'; -export type DependencyGraphProps = React.SVGProps & { - edges: DependencyEdge[]; - nodes: DependencyNode[]; +/** + * Properties of {@link DependencyGraph} + * + * @remarks + * and are useful when rendering custom or edge labels + */ +export interface DependencyGraphProps + extends React.SVGProps { + /** + * Edges of graph + */ + edges: DependencyEdge[]; + /** + * Nodes of Graph + */ + nodes: DependencyNode[]; + /** + * Graph {@link DependencyGraphTypes.Direction | direction} + * + * @remarks + * + * Default: `DependencyGraphTypes.Direction.TOP_BOTTOM` + */ direction?: Direction; + /** + * Node {@link DependencyGraphTypes.Alignment | alignment} + */ align?: Alignment; + /** + * Margin between nodes on each rank + * + * @remarks + * + * Default: 50 + */ nodeMargin?: number; + /** + * Margin between edges + * + * @remarks + * + * Default: 10 + */ edgeMargin?: number; + /** + * Margin between each rank + * + * @remarks + * + * Default: 50 + */ rankMargin?: number; + /** + * Margin on left and right of whole graph + * + * @remarks + * + * Default: 0 + */ paddingX?: number; + /** + * Margin on top and bottom of whole graph + * + * @remarks + * + * Default: 0 + */ paddingY?: number; + /** + * Heuristic used to find set of edges that will make graph acyclic + */ acyclicer?: 'greedy'; + /** + * {@link DependencyGraphTypes.Ranker | Algorithm} used to rank nodes + * + * @remarks + * + * Default: `DependencyGraphTypes.Ranker.NETWORK_SIMPLEX` + */ ranker?: Ranker; + /** + * {@link DependencyGraphTypes.LabelPosition | Position} of label in relation to edge + * + * @remarks + * + * Default: `DependencyGraphTypes.LabelPosition.RIGHT` + */ labelPosition?: LabelPosition; + /** + * How much to move label away from edge + * + * @remarks + * + * Applies only when {@link DependencyGraphProps.labelPosition} is `DependencyGraphTypes.LabelPosition.LEFT` or + * `DependencyGraphTypes.LabelPosition.RIGHT` + */ labelOffset?: number; + /** + * Minimum number of ranks to keep between connected nodes + */ edgeRanks?: number; + /** + * Weight applied to edges in graph + */ edgeWeight?: number; - renderNode?: RenderNodeFunction; - renderLabel?: RenderLabelFunction; + /** + * Custom node rendering component + */ + renderNode?: RenderNodeFunction; + /** + * Custom label rendering component + */ + renderLabel?: RenderLabelFunction; + /** + * {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs | Defs} shared by rendered SVG to be used by + * {@link DependencyGraphProps.renderNode} and/or {@link DependencyGraphProps.renderLabel} + */ defs?: SVGDefsElement | SVGDefsElement[]; + /** + * Controls zoom behavior of graph + * + * @remarks + * + * Default: `enabled` + */ zoom?: 'enabled' | 'disabled' | 'enable-on-click'; -}; +} const WORKSPACE_ID = 'workspace'; -export function DependencyGraph(props: DependencyGraphProps) { +/** + * Graph component used to visualize relations between entities + */ +export function DependencyGraph( + props: DependencyGraphProps, +) { const { edges, nodes, @@ -88,7 +197,7 @@ export function DependencyGraph(props: DependencyGraphProps) { const [containerWidth, setContainerWidth] = React.useState(100); const [containerHeight, setContainerHeight] = React.useState(100); - const graph = React.useRef>( + const graph = React.useRef>>( new dagre.graphlib.Graph(), ); const [graphWidth, setGraphWidth] = React.useState( @@ -256,13 +365,13 @@ export function DependencyGraph(props: DependencyGraphProps) { updateGraph, ]); - function setNode(id: string, node: DependencyNode) { + function setNode(id: string, node: DependencyNode) { graph.current.setNode(id, node); updateGraph(); return graph.current; } - function setEdge(id: dagre.Edge, edge: DependencyEdge) { + function setEdge(id: dagre.Edge, edge: DependencyEdge) { graph.current.setEdge(id, edge); updateGraph(); return graph.current; @@ -303,7 +412,7 @@ export function DependencyGraph(props: DependencyGraphProps) { viewBox={`0 0 ${graphWidth} ${graphHeight}`} > {graphEdges.map(e => { - const edge = graph.current.edge(e) as GraphEdge; + const edge = graph.current.edge(e) as GraphEdge; if (!edge) return null; return ( { - const node = graph.current.node(id) as GraphNode; + const node = graph.current.node(id); if (!node) return null; return ( ( )); const minProps = { - points: [ - { x: 10, y: 20 }, - { x: 20, y: 20 }, - ], id, setEdge, renderElement, diff --git a/packages/core-components/src/components/DependencyGraph/Edge.tsx b/packages/core-components/src/components/DependencyGraph/Edge.tsx index a82c5d4813..fd4d92b834 100644 --- a/packages/core-components/src/components/DependencyGraph/Edge.tsx +++ b/packages/core-components/src/components/DependencyGraph/Edge.tsx @@ -20,13 +20,26 @@ import isFinite from 'lodash/isFinite'; import makeStyles from '@material-ui/core/styles/makeStyles'; import { BackstageTheme } from '@backstage/theme'; import { - GraphEdge, RenderLabelProps, RenderLabelFunction, DependencyEdge, + LabelPosition, } from './types'; import { ARROW_MARKER_ID, EDGE_TEST_ID, LABEL_TEST_ID } from './constants'; import { DefaultLabel } from './DefaultLabel'; +import dagre from 'dagre'; + +/* Based on: https://github.com/dagrejs/dagre/wiki#configuring-the-layout */ +export type EdgeProperties = { + label?: string; + width?: number; + height?: number; + labeloffset?: number; + labelpos?: LabelPosition; + minlen?: number; + weight?: number; +}; +export type GraphEdge = DependencyEdge & dagre.GraphEdge & EdgeProperties; export type DependencyGraphEdgeClassKey = 'path' | 'label'; @@ -47,14 +60,19 @@ const useStyles = makeStyles( type EdgePoint = dagre.GraphEdge['points'][0]; -export type EdgeComponentProps = { +export type EdgeComponentProps = { id: dagre.Edge; edge: GraphEdge; - render?: RenderLabelFunction; - setEdge: (id: dagre.Edge, edge: DependencyEdge) => dagre.graphlib.Graph<{}>; + render?: RenderLabelFunction; + setEdge: ( + id: dagre.Edge, + edge: DependencyEdge, + ) => dagre.graphlib.Graph<{}>; }; -const renderDefault = (props: RenderLabelProps) => ; +const renderDefault = (props: RenderLabelProps) => ( + +); const createPath = d3Shape .line() @@ -62,13 +80,14 @@ const createPath = d3Shape .y(d => d.y) .curve(d3Shape.curveMonotoneX); -export function Edge({ +export function Edge({ render = renderDefault, setEdge, id, edge, -}: EdgeComponentProps) { - const { x = 0, y = 0, width, height, points, ...labelProps } = edge; +}: EdgeComponentProps) { + const { x = 0, y = 0, width, height, points } = edge; + const labelProps: DependencyEdge = edge; const classes = useStyles(); const labelRef = React.useRef(null); diff --git a/packages/core-components/src/components/DependencyGraph/Node.test.tsx b/packages/core-components/src/components/DependencyGraph/Node.test.tsx index ebd6478db9..aaba09c004 100644 --- a/packages/core-components/src/components/DependencyGraph/Node.test.tsx +++ b/packages/core-components/src/components/DependencyGraph/Node.test.tsx @@ -20,21 +20,16 @@ import { render } from '@testing-library/react'; import { Node } from './Node'; import { RenderNodeProps } from './types'; -const node = { id: 'abc' }; +const node = { id: 'abc', x: 0, y: 0, width: 0, height: 0 }; const setNode = jest.fn(() => new dagre.graphlib.Graph()); const renderElement = jest.fn((props: RenderNodeProps) => ( {props.node.id} )); const minProps = { - id: node.id, node, setNode, render: renderElement, - x: 0, - y: 0, - width: 0, - height: 0, }; describe('', () => { @@ -50,7 +45,7 @@ describe('', () => { it('renders the supplied element', () => { const { getByText } = render(); - expect(getByText(minProps.id)).toBeInTheDocument(); + expect(getByText(minProps.node.id)).toBeInTheDocument(); }); it('passes down node properties to the render method', () => { @@ -62,13 +57,13 @@ describe('', () => { it('calls setNode with node ID and actual size after rendering', () => { const { getByText } = render(); - expect(getByText(minProps.id)).toBeInTheDocument(); + expect(getByText(minProps.node.id)).toBeInTheDocument(); // Updates the node in the graph expect(setNode).toHaveBeenCalledWith(node.id, { + ...node, height: 100, width: 100, - ...node, }); // Does not pass down width/height to node diff --git a/packages/core-components/src/components/DependencyGraph/Node.tsx b/packages/core-components/src/components/DependencyGraph/Node.tsx index e37f7a6e37..01d518b554 100644 --- a/packages/core-components/src/components/DependencyGraph/Node.tsx +++ b/packages/core-components/src/components/DependencyGraph/Node.tsx @@ -17,8 +17,9 @@ import React from 'react'; import makeStyles from '@material-ui/core/styles/makeStyles'; import { DefaultNode } from './DefaultNode'; -import { RenderNodeFunction, RenderNodeProps, GraphNode } from './types'; +import { RenderNodeFunction, RenderNodeProps, DependencyNode } from './types'; import { NODE_TEST_ID } from './constants'; +import dagre from 'dagre'; export type DependencyGraphNodeClassKey = 'node'; @@ -31,20 +32,23 @@ const useStyles = makeStyles( { name: 'BackstageDependencyGraphNode' }, ); -export type NodeComponentProps = { +export type GraphNode = dagre.Node>; + +export type NodeComponentProps = { node: GraphNode; - render?: RenderNodeFunction; + render?: RenderNodeFunction; setNode: dagre.graphlib.Graph['setNode']; }; const renderDefault = (props: RenderNodeProps) => ; -export function Node({ +export function Node({ render = renderDefault, setNode, node, -}: NodeComponentProps) { - const { width, height, x = 0, y = 0, ...nodeProps } = node; +}: NodeComponentProps) { + const { width, height, x = 0, y = 0 } = node; + const nodeProps: DependencyNode = node; const classes = useStyles(); const nodeRef = React.useRef(null); diff --git a/packages/core-components/src/components/DependencyGraph/types.ts b/packages/core-components/src/components/DependencyGraph/types.ts index ed22007f9a..eab793685b 100644 --- a/packages/core-components/src/components/DependencyGraph/types.ts +++ b/packages/core-components/src/components/DependencyGraph/types.ts @@ -14,73 +14,132 @@ * limitations under the License. */ -import dagre from 'dagre'; +/** + * Types used to customize and provide data to {@link DependencyGraph} + * + * @packageDocumentation + */ -type CustomType = { [customKey: string]: any }; +import React from 'react'; -/* Edges */ -export type DependencyEdge = T & { +/** + * Edge of {@link DependencyGraph} + */ +export type DependencyEdge = T & { + /** + * ID of {@link DependencyNode} from where the Edge start + */ from: string; + /** + * ID of {@link DependencyNode} to where the Edge goes to + */ to: string; + /** + * Label assigned and rendered with the Edge + */ label?: string; }; -export type GraphEdge = DependencyEdge & - dagre.GraphEdge & - EdgeProperties; +/** + * Properties of {@link DependencyGraphTypes.RenderLabelFunction} for {@link DependencyGraphTypes.DependencyEdge} + */ +export type RenderLabelProps = { edge: DependencyEdge }; -export type RenderLabelProps = { edge: DependencyEdge }; - -export type RenderLabelFunction = ( - props: RenderLabelProps, +/** + * Custom React component for edge labels + */ +export type RenderLabelFunction = ( + props: RenderLabelProps, ) => React.ReactNode; -/* Nodes */ -export type DependencyNode = T & { +/** + * Node of {@link DependencyGraph} + */ +export type DependencyNode = T & { id: string; }; -export type GraphNode = dagre.Node>; +/** + * Properties of {@link DependencyGraphTypes.RenderNodeFunction} for {@link DependencyGraphTypes.DependencyNode} + */ +export type RenderNodeProps = { node: DependencyNode }; -export type RenderNodeProps = { node: DependencyNode }; - -export type RenderNodeFunction = ( - props: RenderNodeProps, +/** + * Custom React component for graph {@link DependencyGraphTypes.DependencyNode} + */ +export type RenderNodeFunction = ( + props: RenderNodeProps, ) => React.ReactNode; -/* Based on: https://github.com/dagrejs/dagre/wiki#configuring-the-layout */ - -export type EdgeProperties = { - label?: string; - width?: number; - height?: number; - labeloffset?: number; - labelpos?: LabelPosition; - minlen?: number; - weight?: number; - [customKey: string]: any; -}; - +/** + * Graph direction + */ export enum Direction { + /** + * Top to Bottom + */ TOP_BOTTOM = 'TB', + /** + * Bottom to Top + */ BOTTOM_TOP = 'BT', + /** + * Left to Right + */ LEFT_RIGHT = 'LR', + /** + * Right to Left + */ RIGHT_LEFT = 'RL', } +/** + * Node alignment + */ export enum Alignment { + /** + * Up Left + */ UP_LEFT = 'UL', + /** + * Up Right + */ UP_RIGHT = 'UR', + /** + * Down Left + */ DOWN_LEFT = 'DL', + /** + * Down Right + */ DOWN_RIGHT = 'DR', } +/** + * Algorithm used to rand nodes in graph + */ export enum Ranker { + /** + * {@link https://en.wikipedia.org/wiki/Network_simplex_algorithm | Network Simplex} algorithm + */ NETWORK_SIMPLEX = 'network-simplex', + /** + * Tight Tree algorithm + */ TIGHT_TREE = 'tight-tree', + /** + * Longest path algorithm + * + * @remarks + * + * Simplest and fastest + */ LONGEST_PATH = 'longest-path', } +/** + * Position of label in relation to the edge + */ export enum LabelPosition { LEFT = 'l', RIGHT = 'r', diff --git a/packages/core-components/src/components/Dialog/Dialog.stories.tsx b/packages/core-components/src/components/Dialog/Dialog.stories.tsx index b4c0a7da57..200124ce9d 100644 --- a/packages/core-components/src/components/Dialog/Dialog.stories.tsx +++ b/packages/core-components/src/components/Dialog/Dialog.stories.tsx @@ -14,15 +14,13 @@ * limitations under the License. */ -import { - Button, - Dialog, - DialogActions, - DialogContent, - DialogTitle, - IconButton, - Typography, -} from '@material-ui/core'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import IconButton from '@material-ui/core/IconButton'; +import Typography from '@material-ui/core/Typography'; import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; import CloseIcon from '@material-ui/icons/Close'; import React, { useState } from 'react'; diff --git a/packages/core-components/src/components/DismissableBanner/DismissableBanner.stories.tsx b/packages/core-components/src/components/DismissableBanner/DismissableBanner.stories.tsx index 583413eb35..a64514be61 100644 --- a/packages/core-components/src/components/DismissableBanner/DismissableBanner.stories.tsx +++ b/packages/core-components/src/components/DismissableBanner/DismissableBanner.stories.tsx @@ -16,7 +16,8 @@ import React from 'react'; import { DismissableBanner } from './DismissableBanner'; -import { Link, Typography } from '@material-ui/core'; +import Link from '@material-ui/core/Link'; +import Typography from '@material-ui/core/Typography'; import { ApiProvider, ApiRegistry, WebStorage } from '@backstage/core-app-api'; import { ErrorApi, diff --git a/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx b/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx index 3d229a5d54..be8e613b61 100644 --- a/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx +++ b/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx @@ -18,7 +18,7 @@ import React, { ReactNode, useState, useEffect } from 'react'; import { useApi, storageApiRef } from '@backstage/core-plugin-api'; import { useObservable } from 'react-use'; import classNames from 'classnames'; -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import { BackstageTheme } from '@backstage/theme'; import Snackbar from '@material-ui/core/Snackbar'; import SnackbarContent from '@material-ui/core/SnackbarContent'; diff --git a/packages/core-components/src/components/Drawer/Drawer.stories.tsx b/packages/core-components/src/components/Drawer/Drawer.stories.tsx index 2538af855e..7e1c8a728f 100644 --- a/packages/core-components/src/components/Drawer/Drawer.stories.tsx +++ b/packages/core-components/src/components/Drawer/Drawer.stories.tsx @@ -15,15 +15,11 @@ */ import React, { useState } from 'react'; -import { - Drawer, - Button, - Typography, - makeStyles, - IconButton, - createStyles, - Theme, -} from '@material-ui/core'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Drawer from '@material-ui/core/Drawer'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; +import IconButton from '@material-ui/core/IconButton'; import Close from '@material-ui/icons/Close'; export default { diff --git a/packages/core-components/src/components/EmptyState/EmptyState.stories.tsx b/packages/core-components/src/components/EmptyState/EmptyState.stories.tsx index ed4dbe2101..53314497fb 100644 --- a/packages/core-components/src/components/EmptyState/EmptyState.stories.tsx +++ b/packages/core-components/src/components/EmptyState/EmptyState.stories.tsx @@ -16,7 +16,7 @@ import React from 'react'; import { EmptyState } from './EmptyState'; -import { Button } from '@material-ui/core'; +import Button from '@material-ui/core/Button'; import { MissingAnnotationEmptyState } from './MissingAnnotationEmptyState'; export default { diff --git a/packages/core-components/src/components/EmptyState/EmptyState.test.tsx b/packages/core-components/src/components/EmptyState/EmptyState.test.tsx index 2f4e4e883d..620e8d4d31 100644 --- a/packages/core-components/src/components/EmptyState/EmptyState.test.tsx +++ b/packages/core-components/src/components/EmptyState/EmptyState.test.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { EmptyState } from './EmptyState'; import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; -import { Button } from '@material-ui/core'; +import Button from '@material-ui/core/Button'; describe('', () => { it('render EmptyState component with type annotaion is missing', async () => { diff --git a/packages/core-components/src/components/EmptyState/EmptyState.tsx b/packages/core-components/src/components/EmptyState/EmptyState.tsx index f3fc3b2399..a7298c4192 100644 --- a/packages/core-components/src/components/EmptyState/EmptyState.tsx +++ b/packages/core-components/src/components/EmptyState/EmptyState.tsx @@ -15,7 +15,9 @@ */ import React from 'react'; -import { makeStyles, Typography, Grid } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Grid from '@material-ui/core/Grid'; import { EmptyStateImage } from './EmptyStateImage'; export type EmptyStateClassKey = 'root' | 'action' | 'imageContainer'; diff --git a/packages/core-components/src/components/EmptyState/EmptyStateImage.tsx b/packages/core-components/src/components/EmptyState/EmptyStateImage.tsx index 012fb04e76..f82d8e6e57 100644 --- a/packages/core-components/src/components/EmptyState/EmptyStateImage.tsx +++ b/packages/core-components/src/components/EmptyState/EmptyStateImage.tsx @@ -19,7 +19,7 @@ import missingAnnotation from './assets/missingAnnotation.svg'; import noInformation from './assets/noInformation.svg'; import createComponent from './assets/createComponent.svg'; import noBuild from './assets/noBuild.svg'; -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; type Props = { missing: 'field' | 'info' | 'content' | 'data'; diff --git a/packages/core-components/src/components/EmptyState/MissingAnnotationEmptyState.tsx b/packages/core-components/src/components/EmptyState/MissingAnnotationEmptyState.tsx index 6f20f7c8e6..61131bad9b 100644 --- a/packages/core-components/src/components/EmptyState/MissingAnnotationEmptyState.tsx +++ b/packages/core-components/src/components/EmptyState/MissingAnnotationEmptyState.tsx @@ -15,7 +15,9 @@ */ import React from 'react'; -import { Button, makeStyles, Typography } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; import { BackstageTheme } from '@backstage/theme'; import { Link } from '../Link'; import { EmptyState } from './EmptyState'; diff --git a/packages/core-components/src/components/ErrorPanel/ErrorPanel.tsx b/packages/core-components/src/components/ErrorPanel/ErrorPanel.tsx index 22f28a7a73..3ef35d6eaa 100644 --- a/packages/core-components/src/components/ErrorPanel/ErrorPanel.tsx +++ b/packages/core-components/src/components/ErrorPanel/ErrorPanel.tsx @@ -14,7 +14,10 @@ * limitations under the License. */ -import { List, ListItem, ListItemText, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; import React, { PropsWithChildren } from 'react'; import { CopyTextButton } from '../CopyTextButton'; import { WarningPanel } from '../WarningPanel'; diff --git a/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.tsx b/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.tsx index 7c4b9736b7..3152d701c8 100644 --- a/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.tsx +++ b/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.tsx @@ -14,7 +14,9 @@ * limitations under the License. */ -import { ClickAwayListener, makeStyles, Typography } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import ClickAwayListener from '@material-ui/core/ClickAwayListener'; +import Typography from '@material-ui/core/Typography'; import React, { PropsWithChildren, useCallback, diff --git a/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.tsx b/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.tsx index c4508eb9bc..4e83976a96 100644 --- a/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.tsx +++ b/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; import { IconLinkVertical, IconLinkVerticalProps } from './IconLinkVertical'; -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; export type HeaderIconLinkRowClassKey = 'links'; diff --git a/packages/core-components/src/components/HeaderIconLinkRow/IconLinkVertical.tsx b/packages/core-components/src/components/HeaderIconLinkRow/IconLinkVertical.tsx index 532ba2f959..421147753b 100644 --- a/packages/core-components/src/components/HeaderIconLinkRow/IconLinkVertical.tsx +++ b/packages/core-components/src/components/HeaderIconLinkRow/IconLinkVertical.tsx @@ -15,7 +15,8 @@ */ import React from 'react'; import classnames from 'classnames'; -import { makeStyles, Link } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Link from '@material-ui/core/Link'; import LinkIcon from '@material-ui/icons/Link'; import { Link as RouterLink } from '../Link'; diff --git a/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.test.tsx b/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.test.tsx index 080e48919b..a3ef93d9fa 100644 --- a/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.test.tsx +++ b/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.test.tsx @@ -18,7 +18,7 @@ import React from 'react'; import { fireEvent } from '@testing-library/react'; import { renderInTestApp } from '@backstage/test-utils'; import { HorizontalScrollGrid } from './HorizontalScrollGrid'; -import { Grid } from '@material-ui/core'; +import Grid from '@material-ui/core/Grid'; describe('', () => { beforeEach(() => { diff --git a/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.tsx b/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.tsx index 88b61d56bb..483d27d657 100644 --- a/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.tsx +++ b/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.tsx @@ -18,7 +18,9 @@ import React, { PropsWithChildren } from 'react'; import classNames from 'classnames'; import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'; import ChevronRightIcon from '@material-ui/icons/ChevronRight'; -import { Grid, IconButton, makeStyles, Theme } from '@material-ui/core'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import Grid from '@material-ui/core/Grid'; +import IconButton from '@material-ui/core/IconButton'; const generateGradientStops = (themeType: 'dark' | 'light') => { // 97% corresponds to the theme.palette.background.default for the light theme diff --git a/packages/core-components/src/components/Lifecycle/Lifecycle.tsx b/packages/core-components/src/components/Lifecycle/Lifecycle.tsx index 76e63854f5..f553129927 100644 --- a/packages/core-components/src/components/Lifecycle/Lifecycle.tsx +++ b/packages/core-components/src/components/Lifecycle/Lifecycle.tsx @@ -16,7 +16,7 @@ import React from 'react'; import CSS from 'csstype'; -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; type Props = CSS.Properties & { shorthand?: boolean; diff --git a/packages/core-components/src/components/Link/Link.tsx b/packages/core-components/src/components/Link/Link.tsx index d20773a8bb..8866150f86 100644 --- a/packages/core-components/src/components/Link/Link.tsx +++ b/packages/core-components/src/components/Link/Link.tsx @@ -15,10 +15,9 @@ */ import { useAnalytics } from '@backstage/core-plugin-api'; -import { - Link as MaterialLink, +import MaterialLink, { LinkProps as MaterialLinkProps, -} from '@material-ui/core'; +} from '@material-ui/core/Link'; import React, { ElementType } from 'react'; import { Link as RouterLink, diff --git a/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx b/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx index 65be789bb4..eacd9adc8a 100644 --- a/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx +++ b/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import ReactMarkdown from 'react-markdown'; import gfm from 'remark-gfm'; import React from 'react'; diff --git a/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx b/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx index 7f7f51abba..b911a9a1d9 100644 --- a/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx +++ b/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx @@ -14,15 +14,12 @@ * limitations under the License. */ -import { - ListItem, - ListItemAvatar, - ListItemText, - makeStyles, - Typography, - Theme, - Button, -} from '@material-ui/core'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemAvatar from '@material-ui/core/ListItemAvatar'; +import ListItemText from '@material-ui/core/ListItemText'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; import React, { useState } from 'react'; import { PendingAuthRequest } from '@backstage/core-plugin-api'; diff --git a/packages/core-components/src/components/OAuthRequestDialog/OAuthRequestDialog.tsx b/packages/core-components/src/components/OAuthRequestDialog/OAuthRequestDialog.tsx index 73e276fce9..5378fa6c15 100644 --- a/packages/core-components/src/components/OAuthRequestDialog/OAuthRequestDialog.tsx +++ b/packages/core-components/src/components/OAuthRequestDialog/OAuthRequestDialog.tsx @@ -14,16 +14,13 @@ * limitations under the License. */ -import { - Dialog, - DialogActions, - DialogContent, - DialogTitle, - List, - makeStyles, - Theme, - Button, -} from '@material-ui/core'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import List from '@material-ui/core/List'; +import Button from '@material-ui/core/Button'; import React, { useMemo, useState } from 'react'; import { useObservable } from 'react-use'; import LoginRequestListItem from './LoginRequestListItem'; diff --git a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.stories.tsx b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.stories.tsx index 0882a63e9c..a022b3b5c2 100644 --- a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.stories.tsx +++ b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.stories.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Box } from '@material-ui/core'; +import Box from '@material-ui/core/Box'; import React from 'react'; import { OverflowTooltip } from './OverflowTooltip'; diff --git a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx index a06930cdfa..fb21813f89 100644 --- a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx +++ b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ -import { makeStyles, Tooltip, TooltipProps } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Tooltip, { TooltipProps } from '@material-ui/core/Tooltip'; import React, { useState } from 'react'; import TextTruncate, { TextTruncateProps } from 'react-text-truncate'; diff --git a/packages/core-components/src/components/Progress/Progress.tsx b/packages/core-components/src/components/Progress/Progress.tsx index 00ad532b5e..f199c5640f 100644 --- a/packages/core-components/src/components/Progress/Progress.tsx +++ b/packages/core-components/src/components/Progress/Progress.tsx @@ -15,7 +15,9 @@ */ import React, { useState, useEffect, PropsWithChildren } from 'react'; -import { LinearProgress, LinearProgressProps } from '@material-ui/core'; +import LinearProgress, { + LinearProgressProps, +} from '@material-ui/core/LinearProgress'; export function Progress(props: PropsWithChildren) { const [isVisible, setIsVisible] = useState(false); diff --git a/packages/core-components/src/components/ProgressBars/Gauge.tsx b/packages/core-components/src/components/ProgressBars/Gauge.tsx index 2699f8a289..2f6973e4d8 100644 --- a/packages/core-components/src/components/ProgressBars/Gauge.tsx +++ b/packages/core-components/src/components/ProgressBars/Gauge.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { makeStyles, useTheme } from '@material-ui/core'; +import { makeStyles, useTheme } from '@material-ui/core/styles'; import { BackstageTheme } from '@backstage/theme'; import { Circle } from 'rc-progress'; import React from 'react'; diff --git a/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx b/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx index da81100a99..ad3323f8d3 100644 --- a/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx +++ b/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx @@ -16,7 +16,7 @@ import React, { PropsWithChildren } from 'react'; import { GaugeCard } from './GaugeCard'; -import { Grid } from '@material-ui/core'; +import Grid from '@material-ui/core/Grid'; import { MemoryRouter } from 'react-router'; const linkInfo = { title: 'Go to XYZ Location', link: '#' }; diff --git a/packages/core-components/src/components/ProgressBars/GaugeCard.tsx b/packages/core-components/src/components/ProgressBars/GaugeCard.tsx index aec63add11..5c5a825921 100644 --- a/packages/core-components/src/components/ProgressBars/GaugeCard.tsx +++ b/packages/core-components/src/components/ProgressBars/GaugeCard.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import { InfoCard, InfoCardVariants } from '../../layout/InfoCard'; import { BottomLinkProps } from '../../layout/BottomLink'; import { Gauge } from './Gauge'; diff --git a/packages/core-components/src/components/ProgressBars/LinearGauge.tsx b/packages/core-components/src/components/ProgressBars/LinearGauge.tsx index cf8c022852..afc93fd337 100644 --- a/packages/core-components/src/components/ProgressBars/LinearGauge.tsx +++ b/packages/core-components/src/components/ProgressBars/LinearGauge.tsx @@ -15,7 +15,8 @@ */ import React from 'react'; -import { Tooltip, useTheme } from '@material-ui/core'; +import { useTheme } from '@material-ui/core/styles'; +import Tooltip from '@material-ui/core/Tooltip'; // @ts-ignore import { Line } from 'rc-progress'; import { BackstageTheme } from '@backstage/theme'; diff --git a/packages/core-components/src/components/ResponseErrorPanel/ResponseErrorPanel.tsx b/packages/core-components/src/components/ResponseErrorPanel/ResponseErrorPanel.tsx index 37143000a3..68d7360400 100644 --- a/packages/core-components/src/components/ResponseErrorPanel/ResponseErrorPanel.tsx +++ b/packages/core-components/src/components/ResponseErrorPanel/ResponseErrorPanel.tsx @@ -15,7 +15,10 @@ */ import { ResponseError } from '@backstage/errors'; -import { Divider, ListItem, ListItemText, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Divider from '@material-ui/core/Divider'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; import React from 'react'; import { CodeSnippet } from '../CodeSnippet'; import { CopyTextButton } from '../CopyTextButton'; diff --git a/packages/core-components/src/components/Select/Select.tsx b/packages/core-components/src/components/Select/Select.tsx index 23ed2cf62c..a0e3aba94b 100644 --- a/packages/core-components/src/components/Select/Select.tsx +++ b/packages/core-components/src/components/Select/Select.tsx @@ -14,16 +14,14 @@ * limitations under the License. */ -import { - Checkbox, - Chip, - ClickAwayListener, - FormControl, - InputBase, - MenuItem, - Select, - Typography, -} from '@material-ui/core'; +import Checkbox from '@material-ui/core/Checkbox'; +import Chip from '@material-ui/core/Chip'; +import ClickAwayListener from '@material-ui/core/ClickAwayListener'; +import FormControl from '@material-ui/core/FormControl'; +import InputBase from '@material-ui/core/InputBase'; +import MenuItem from '@material-ui/core/MenuItem'; +import Select from '@material-ui/core/Select'; +import Typography from '@material-ui/core/Typography'; import { createStyles, makeStyles, diff --git a/packages/core-components/src/components/Select/static/ClosedDropdown.tsx b/packages/core-components/src/components/Select/static/ClosedDropdown.tsx index 61a24abcef..eb93f5a354 100644 --- a/packages/core-components/src/components/Select/static/ClosedDropdown.tsx +++ b/packages/core-components/src/components/Select/static/ClosedDropdown.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ import React from 'react'; -import { SvgIcon, makeStyles, createStyles } from '@material-ui/core'; +import { makeStyles, createStyles } from '@material-ui/core/styles'; +import SvgIcon from '@material-ui/core/SvgIcon'; export type ClosedDropdownClassKey = 'icon'; diff --git a/packages/core-components/src/components/Select/static/OpenedDropdown.tsx b/packages/core-components/src/components/Select/static/OpenedDropdown.tsx index eb86a34fe0..617288ded6 100644 --- a/packages/core-components/src/components/Select/static/OpenedDropdown.tsx +++ b/packages/core-components/src/components/Select/static/OpenedDropdown.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ import React from 'react'; -import { SvgIcon, makeStyles, createStyles } from '@material-ui/core'; +import { makeStyles, createStyles } from '@material-ui/core/styles'; +import SvgIcon from '@material-ui/core/SvgIcon'; export type OpenedDropdownClassKey = 'icon'; diff --git a/packages/core-components/src/components/SimpleStepper/SimpleStepper.stories.tsx b/packages/core-components/src/components/SimpleStepper/SimpleStepper.stories.tsx index 9d958f766d..aa6846bc8f 100644 --- a/packages/core-components/src/components/SimpleStepper/SimpleStepper.stories.tsx +++ b/packages/core-components/src/components/SimpleStepper/SimpleStepper.stories.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { TextField } from '@material-ui/core'; +import TextField from '@material-ui/core/TextField'; import React, { useState } from 'react'; import { SimpleStepper } from './SimpleStepper'; import { SimpleStepperStep } from './SimpleStepperStep'; diff --git a/packages/core-components/src/components/SimpleStepper/SimpleStepper.tsx b/packages/core-components/src/components/SimpleStepper/SimpleStepper.tsx index 9ffb63c085..4a1a4ab919 100644 --- a/packages/core-components/src/components/SimpleStepper/SimpleStepper.tsx +++ b/packages/core-components/src/components/SimpleStepper/SimpleStepper.tsx @@ -20,7 +20,7 @@ import React, { useEffect, PropsWithChildren, } from 'react'; -import { Stepper as MuiStepper } from '@material-ui/core'; +import MuiStepper from '@material-ui/core/Stepper'; type InternalState = { stepperLength: number; diff --git a/packages/core-components/src/components/SimpleStepper/SimpleStepperFooter.tsx b/packages/core-components/src/components/SimpleStepper/SimpleStepperFooter.tsx index 270099a104..5f4bdbf0c6 100644 --- a/packages/core-components/src/components/SimpleStepper/SimpleStepperFooter.tsx +++ b/packages/core-components/src/components/SimpleStepper/SimpleStepperFooter.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ import React, { useContext, ReactNode, PropsWithChildren } from 'react'; -import { Button, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; import { StepActions } from './types'; import { VerticalStepperContext } from './SimpleStepper'; diff --git a/packages/core-components/src/components/SimpleStepper/SimpleStepperStep.tsx b/packages/core-components/src/components/SimpleStepper/SimpleStepperStep.tsx index 0696a1256a..bb46163105 100644 --- a/packages/core-components/src/components/SimpleStepper/SimpleStepperStep.tsx +++ b/packages/core-components/src/components/SimpleStepper/SimpleStepperStep.tsx @@ -14,13 +14,11 @@ * limitations under the License. */ import React, { PropsWithChildren } from 'react'; -import { - Step as MuiStep, - StepContent, - StepLabel, - Typography, - makeStyles, -} from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import MuiStep from '@material-ui/core/Step'; +import StepContent from '@material-ui/core/StepContent'; +import StepLabel from '@material-ui/core/StepLabel'; +import Typography from '@material-ui/core/Typography'; import { SimpleStepperFooter } from './SimpleStepperFooter'; import { StepProps } from './types'; diff --git a/packages/core-components/src/components/Status/Status.tsx b/packages/core-components/src/components/Status/Status.tsx index a6215d6127..a855fe3ebe 100644 --- a/packages/core-components/src/components/Status/Status.tsx +++ b/packages/core-components/src/components/Status/Status.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import { BackstageTheme } from '@backstage/theme'; import classNames from 'classnames'; import React, { PropsWithChildren } from 'react'; diff --git a/packages/core-components/src/components/StructuredMetadataTable/MetadataTable.tsx b/packages/core-components/src/components/StructuredMetadataTable/MetadataTable.tsx index 3c869f88c3..2f5dc0b6b5 100644 --- a/packages/core-components/src/components/StructuredMetadataTable/MetadataTable.tsx +++ b/packages/core-components/src/components/StructuredMetadataTable/MetadataTable.tsx @@ -16,15 +16,15 @@ import React from 'react'; import { - Table, - TableBody, - TableCell, - TableRow, withStyles, createStyles, WithStyles, Theme, -} from '@material-ui/core'; +} from '@material-ui/core/styles'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableRow from '@material-ui/core/TableRow'; export type MetadataTableTitleCellClassKey = 'root'; diff --git a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.stories.tsx b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.stories.tsx index 458c431b27..f48e959073 100644 --- a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.stories.tsx +++ b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.stories.tsx @@ -15,7 +15,7 @@ */ import React, { PropsWithChildren } from 'react'; import { InfoCard } from '../../layout/InfoCard'; -import { Grid } from '@material-ui/core'; +import Grid from '@material-ui/core/Grid'; import { StructuredMetadataTable } from './StructuredMetadataTable'; const cardContentStyle = { heightX: 200, width: 500 }; diff --git a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx index abaa662159..9ef7890820 100644 --- a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx +++ b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx @@ -15,7 +15,12 @@ */ import React, { Fragment, ReactElement } from 'react'; -import { withStyles, createStyles, WithStyles, Theme } from '@material-ui/core'; +import { + withStyles, + createStyles, + WithStyles, + Theme, +} from '@material-ui/core/styles'; import startCase from 'lodash/startCase'; import { diff --git a/packages/core-components/src/components/SupportButton/SupportButton.tsx b/packages/core-components/src/components/SupportButton/SupportButton.tsx index bee2928314..841bfe9429 100644 --- a/packages/core-components/src/components/SupportButton/SupportButton.tsx +++ b/packages/core-components/src/components/SupportButton/SupportButton.tsx @@ -16,20 +16,18 @@ import { useApp } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; -import { - Box, - Button, - DialogActions, - IconButton, - List, - ListItem, - ListItemIcon, - ListItemText, - makeStyles, - Popover, - Typography, - useMediaQuery, -} from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; +import Box from '@material-ui/core/Box'; +import Button from '@material-ui/core/Button'; +import DialogActions from '@material-ui/core/DialogActions'; +import IconButton from '@material-ui/core/IconButton'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import Popover from '@material-ui/core/Popover'; +import Typography from '@material-ui/core/Typography'; import React, { MouseEventHandler, useState } from 'react'; import { SupportItem, SupportItemLink, useSupportConfig } from '../../hooks'; import { HelpIcon } from '../../icons'; diff --git a/packages/core-components/src/components/TabbedLayout/TabbedLayout.tsx b/packages/core-components/src/components/TabbedLayout/TabbedLayout.tsx index 03f012b78f..26e70e33b5 100644 --- a/packages/core-components/src/components/TabbedLayout/TabbedLayout.tsx +++ b/packages/core-components/src/components/TabbedLayout/TabbedLayout.tsx @@ -23,7 +23,7 @@ import React, { ReactNode, } from 'react'; import { RoutedTabs } from './RoutedTabs'; -import { TabProps } from '@material-ui/core'; +import { TabProps } from '@material-ui/core/Tab'; type SubRoute = { path: string; diff --git a/packages/core-components/src/components/TabbedLayout/types.ts b/packages/core-components/src/components/TabbedLayout/types.ts index b70ab3da0a..59e26305bd 100644 --- a/packages/core-components/src/components/TabbedLayout/types.ts +++ b/packages/core-components/src/components/TabbedLayout/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { TabProps } from '@material-ui/core'; +import { TabProps } from '@material-ui/core/Tab'; import * as React from 'react'; export type SubRoute = { diff --git a/packages/core-components/src/components/Table/Filters.tsx b/packages/core-components/src/components/Table/Filters.tsx index 7498138751..0f4143d442 100644 --- a/packages/core-components/src/components/Table/Filters.tsx +++ b/packages/core-components/src/components/Table/Filters.tsx @@ -16,7 +16,8 @@ import React, { useEffect, useState } from 'react'; import { BackstageTheme } from '@backstage/theme'; -import { Button, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; import { Select } from '../Select'; import { SelectProps } from '../Select/Select'; diff --git a/packages/core-components/src/components/Table/SubvalueCell.tsx b/packages/core-components/src/components/Table/SubvalueCell.tsx index 43fb54fca8..2564e8e4eb 100644 --- a/packages/core-components/src/components/Table/SubvalueCell.tsx +++ b/packages/core-components/src/components/Table/SubvalueCell.tsx @@ -16,7 +16,7 @@ import React from 'react'; import { BackstageTheme } from '@backstage/theme'; -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; export type SubvalueCellClassKey = 'value' | 'subvalue'; diff --git a/packages/core-components/src/components/Table/Table.stories.tsx b/packages/core-components/src/components/Table/Table.stories.tsx index 27fd33f32c..7785764b22 100644 --- a/packages/core-components/src/components/Table/Table.stories.tsx +++ b/packages/core-components/src/components/Table/Table.stories.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import React from 'react'; import { Link } from '../Link'; import { SubvalueCell, Table, TableColumn } from '.'; diff --git a/packages/core-components/src/components/Table/Table.tsx b/packages/core-components/src/components/Table/Table.tsx index 61e9cc7969..f61811938e 100644 --- a/packages/core-components/src/components/Table/Table.tsx +++ b/packages/core-components/src/components/Table/Table.tsx @@ -15,13 +15,9 @@ */ import { BackstageTheme } from '@backstage/theme'; -import { - IconButton, - makeStyles, - Typography, - useTheme, - withStyles, -} from '@material-ui/core'; +import { makeStyles, useTheme, withStyles } from '@material-ui/core/styles'; +import IconButton from '@material-ui/core/IconButton'; +import Typography from '@material-ui/core/Typography'; // Material-table is not using the standard icons available in in material-ui. https://github.com/mbrn/material-table/issues/51 import AddBox from '@material-ui/icons/AddBox'; import ArrowUpward from '@material-ui/icons/ArrowUpward'; diff --git a/packages/core-components/src/components/Tabs/Tab.tsx b/packages/core-components/src/components/Tabs/Tab.tsx index 7b284940f1..571640d970 100644 --- a/packages/core-components/src/components/Tabs/Tab.tsx +++ b/packages/core-components/src/components/Tabs/Tab.tsx @@ -15,7 +15,8 @@ */ import React from 'react'; -import { Tab, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Tab from '@material-ui/core/Tab'; import { BackstageTheme } from '@backstage/theme'; interface StyledTabProps { diff --git a/packages/core-components/src/components/Tabs/TabBar.tsx b/packages/core-components/src/components/Tabs/TabBar.tsx index d0febebc6a..d2f0250fd4 100644 --- a/packages/core-components/src/components/Tabs/TabBar.tsx +++ b/packages/core-components/src/components/Tabs/TabBar.tsx @@ -15,7 +15,8 @@ */ import React, { PropsWithChildren } from 'react'; -import { Tabs, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Tabs from '@material-ui/core/Tabs'; import { BackstageTheme } from '@backstage/theme'; interface StyledTabsProps { diff --git a/packages/core-components/src/components/Tabs/TabIcon.tsx b/packages/core-components/src/components/Tabs/TabIcon.tsx index 37b38217a3..9743469aa7 100644 --- a/packages/core-components/src/components/Tabs/TabIcon.tsx +++ b/packages/core-components/src/components/Tabs/TabIcon.tsx @@ -15,7 +15,8 @@ */ import React from 'react'; -import { IconButton, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import IconButton from '@material-ui/core/IconButton'; import { BackstageTheme } from '@backstage/theme'; interface StyledIconProps { diff --git a/packages/core-components/src/components/Tabs/Tabs.tsx b/packages/core-components/src/components/Tabs/Tabs.tsx index d60f3ff639..31bf1d2027 100644 --- a/packages/core-components/src/components/Tabs/Tabs.tsx +++ b/packages/core-components/src/components/Tabs/Tabs.tsx @@ -16,7 +16,7 @@ import React, { useRef, useEffect, MutableRefObject, useState } from 'react'; import { BackstageTheme } from '@backstage/theme'; -import { AppBar } from '@material-ui/core'; +import AppBar from '@material-ui/core/AppBar'; import { makeStyles } from '@material-ui/core/styles'; import NavigateBeforeIcon from '@material-ui/icons/NavigateBefore'; import NavigateNextIcon from '@material-ui/icons/NavigateNext'; diff --git a/packages/core-components/src/components/TrendLine/TrendLine.tsx b/packages/core-components/src/components/TrendLine/TrendLine.tsx index cb44d2b36b..b59d748c2c 100644 --- a/packages/core-components/src/components/TrendLine/TrendLine.tsx +++ b/packages/core-components/src/components/TrendLine/TrendLine.tsx @@ -21,7 +21,7 @@ import { SparklinesLineProps, SparklinesProps, } from 'react-sparklines'; -import { useTheme } from '@material-ui/core'; +import { useTheme } from '@material-ui/core/styles'; import { BackstageTheme } from '@backstage/theme'; function color(data: number[], theme: BackstageTheme): string | undefined { diff --git a/packages/core-components/src/components/WarningPanel/WarningPanel.stories.tsx b/packages/core-components/src/components/WarningPanel/WarningPanel.stories.tsx index bab831e905..838cb02a76 100644 --- a/packages/core-components/src/components/WarningPanel/WarningPanel.stories.tsx +++ b/packages/core-components/src/components/WarningPanel/WarningPanel.stories.tsx @@ -16,7 +16,9 @@ import React from 'react'; import { WarningPanel } from './WarningPanel'; -import { Button, Link, Typography } from '@material-ui/core'; +import Button from '@material-ui/core/Button'; +import Link from '@material-ui/core/Link'; +import Typography from '@material-ui/core/Typography'; export default { title: 'Feedback/Warning Panel', diff --git a/packages/core-components/src/components/WarningPanel/WarningPanel.test.tsx b/packages/core-components/src/components/WarningPanel/WarningPanel.test.tsx index 4ae6423333..25cf642c28 100644 --- a/packages/core-components/src/components/WarningPanel/WarningPanel.test.tsx +++ b/packages/core-components/src/components/WarningPanel/WarningPanel.test.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { fireEvent, screen } from '@testing-library/react'; import { renderInTestApp } from '@backstage/test-utils'; -import { Typography } from '@material-ui/core'; +import Typography from '@material-ui/core/Typography'; import { WarningPanel, WarningProps } from './WarningPanel'; diff --git a/packages/core-components/src/components/WarningPanel/WarningPanel.tsx b/packages/core-components/src/components/WarningPanel/WarningPanel.tsx index bf845617c9..0cf250497b 100644 --- a/packages/core-components/src/components/WarningPanel/WarningPanel.tsx +++ b/packages/core-components/src/components/WarningPanel/WarningPanel.tsx @@ -14,16 +14,12 @@ * limitations under the License. */ import { BackstageTheme } from '@backstage/theme'; -import { - Accordion, - AccordionSummary, - AccordionDetails, - Grid, - makeStyles, - Typography, - darken, - lighten, -} from '@material-ui/core'; +import { makeStyles, darken, lighten } from '@material-ui/core/styles'; +import Accordion from '@material-ui/core/Accordion'; +import AccordionSummary from '@material-ui/core/AccordionSummary'; +import AccordionDetails from '@material-ui/core/AccordionDetails'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; import ErrorOutline from '@material-ui/icons/ErrorOutline'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import React from 'react'; diff --git a/packages/core-components/src/layout/BottomLink/BottomLink.tsx b/packages/core-components/src/layout/BottomLink/BottomLink.tsx index cb168600e5..e097724385 100644 --- a/packages/core-components/src/layout/BottomLink/BottomLink.tsx +++ b/packages/core-components/src/layout/BottomLink/BottomLink.tsx @@ -15,7 +15,9 @@ */ import React from 'react'; -import { Divider, Typography, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Divider from '@material-ui/core/Divider'; +import Typography from '@material-ui/core/Typography'; import ArrowIcon from '@material-ui/icons/ArrowForward'; import { BackstageTheme } from '@backstage/theme'; import Box from '@material-ui/core/Box'; diff --git a/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.stories.tsx b/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.stories.tsx index 9716471d9f..12bd757aa3 100644 --- a/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.stories.tsx +++ b/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.stories.tsx @@ -13,7 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Box, List, ListItem, Popover, Typography } from '@material-ui/core'; + +import Box from '@material-ui/core/Box'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import Popover from '@material-ui/core/Popover'; +import Typography from '@material-ui/core/Typography'; import ExpandLessIcon from '@material-ui/icons/ExpandLess'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import React, { Fragment } from 'react'; diff --git a/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.test.tsx b/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.test.tsx index 4c8d707501..12125bafc9 100644 --- a/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.test.tsx +++ b/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.test.tsx @@ -15,7 +15,7 @@ */ import { renderInTestApp } from '@backstage/test-utils'; -import { Typography } from '@material-ui/core'; +import Typography from '@material-ui/core/Typography'; import { fireEvent } from '@testing-library/react'; import React from 'react'; import { Link } from '../../components/Link'; diff --git a/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.tsx b/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.tsx index 26304f9e2b..4423c5c0b7 100644 --- a/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.tsx +++ b/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.tsx @@ -14,15 +14,13 @@ * limitations under the License. */ -import { - Box, - Breadcrumbs as MaterialBreadcrumbs, - List, - ListItem, - Popover, - Typography, - withStyles, -} from '@material-ui/core'; +import { withStyles } from '@material-ui/core/styles'; +import Box from '@material-ui/core/Box'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import Popover from '@material-ui/core/Popover'; +import Typography from '@material-ui/core/Typography'; +import MaterialBreadcrumbs from '@material-ui/core/Breadcrumbs'; import React, { ComponentProps, Fragment } from 'react'; type Props = ComponentProps; diff --git a/packages/core-components/src/layout/Content/Content.tsx b/packages/core-components/src/layout/Content/Content.tsx index 588c9fd630..122eb07e2a 100644 --- a/packages/core-components/src/layout/Content/Content.tsx +++ b/packages/core-components/src/layout/Content/Content.tsx @@ -16,7 +16,7 @@ import React, { PropsWithChildren } from 'react'; import classNames from 'classnames'; -import { Theme, makeStyles } from '@material-ui/core'; +import { Theme, makeStyles } from '@material-ui/core/styles'; export type BackstageContentClassKey = 'root' | 'stretch' | 'noPadding'; diff --git a/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx b/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx index c3faec64a2..b4a80ac4b1 100644 --- a/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx +++ b/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx @@ -19,7 +19,8 @@ */ import React, { PropsWithChildren, ReactNode } from 'react'; -import { Typography, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; import { Helmet } from 'react-helmet'; export type ContentHeaderClassKey = diff --git a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx index 79aaaebfc6..46257f85bb 100644 --- a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx +++ b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx @@ -15,7 +15,9 @@ */ import React from 'react'; -import { Typography, Link, Grid } from '@material-ui/core'; +import Typography from '@material-ui/core/Typography'; +import Link from '@material-ui/core/Link'; +import Grid from '@material-ui/core/Grid'; import { makeStyles } from '@material-ui/core/styles'; import { BackstageTheme } from '@backstage/theme'; import { MicDrop } from './MicDrop'; diff --git a/packages/core-components/src/layout/ErrorPage/MicDrop.tsx b/packages/core-components/src/layout/ErrorPage/MicDrop.tsx index 5500994e4f..efa101b23b 100644 --- a/packages/core-components/src/layout/ErrorPage/MicDrop.tsx +++ b/packages/core-components/src/layout/ErrorPage/MicDrop.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import MicDropSvgUrl from './mic-drop.svg'; const useStyles = makeStyles( diff --git a/packages/core-components/src/layout/Header/Header.tsx b/packages/core-components/src/layout/Header/Header.tsx index 535e3c4ed8..aed9e9f8e4 100644 --- a/packages/core-components/src/layout/Header/Header.tsx +++ b/packages/core-components/src/layout/Header/Header.tsx @@ -16,7 +16,11 @@ import { useApi, configApiRef } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; -import { Box, Grid, makeStyles, Tooltip, Typography } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Box from '@material-ui/core/Box'; +import Grid from '@material-ui/core/Grid'; +import Tooltip from '@material-ui/core/Tooltip'; +import Typography from '@material-ui/core/Typography'; import React, { CSSProperties, PropsWithChildren, ReactNode } from 'react'; import { Helmet } from 'react-helmet'; import { Link } from '../../components/Link'; diff --git a/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx b/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx index 5243a65498..d8dc398ce3 100644 --- a/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx +++ b/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx @@ -15,15 +15,14 @@ */ import React, { Fragment, ReactElement, ComponentType } from 'react'; -import { - IconButton, - List, - ListItem, - ListItemIcon, - ListItemText, - Popover, +import IconButton from '@material-ui/core/IconButton'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText, { ListItemTextProps, -} from '@material-ui/core'; +} from '@material-ui/core/ListItemText'; +import Popover from '@material-ui/core/Popover'; import { VerticalMenuIcon } from './VerticalMenuIcon'; type ActionItemProps = { diff --git a/packages/core-components/src/layout/HeaderLabel/HeaderLabel.tsx b/packages/core-components/src/layout/HeaderLabel/HeaderLabel.tsx index 5071971f48..3b9bde0f62 100644 --- a/packages/core-components/src/layout/HeaderLabel/HeaderLabel.tsx +++ b/packages/core-components/src/layout/HeaderLabel/HeaderLabel.tsx @@ -14,7 +14,10 @@ * limitations under the License. */ -import { Link, makeStyles, Typography, Grid } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Link from '@material-ui/core/Link'; +import Typography from '@material-ui/core/Typography'; +import Grid from '@material-ui/core/Grid'; import React from 'react'; export type HeaderLabelClassKey = 'root' | 'label' | 'value'; diff --git a/packages/core-components/src/layout/HeaderTabs/HeaderTabs.test.tsx b/packages/core-components/src/layout/HeaderTabs/HeaderTabs.test.tsx index e6762de90e..2501785701 100644 --- a/packages/core-components/src/layout/HeaderTabs/HeaderTabs.test.tsx +++ b/packages/core-components/src/layout/HeaderTabs/HeaderTabs.test.tsx @@ -17,7 +17,8 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; import { HeaderTabs } from '.'; -import { Badge, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Badge from '@material-ui/core/Badge'; const mockTabs = [ { id: 'overview', label: 'Overview' }, diff --git a/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx b/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx index b34af3eb83..dd740d1c53 100644 --- a/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx +++ b/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx @@ -18,7 +18,9 @@ // This is just a temporary solution to implementing tabs for now import React, { useState, useEffect } from 'react'; -import { makeStyles, Tabs, Tab as TabUI, TabProps } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import TabUI, { TabProps } from '@material-ui/core/Tab'; +import Tabs from '@material-ui/core/Tabs'; export type HeaderTabsClassKey = | 'tabsWrapper' diff --git a/packages/core-components/src/layout/HomepageTimer/HomepageTimer.test.tsx b/packages/core-components/src/layout/HomepageTimer/HomepageTimer.test.tsx index eda877884e..1d4bf6954a 100644 --- a/packages/core-components/src/layout/HomepageTimer/HomepageTimer.test.tsx +++ b/packages/core-components/src/layout/HomepageTimer/HomepageTimer.test.tsx @@ -18,7 +18,7 @@ import { renderWithEffects } from '@backstage/test-utils'; import { HomepageTimer } from './HomepageTimer'; import React from 'react'; import { lightTheme } from '@backstage/theme'; -import { ThemeProvider } from '@material-ui/core'; +import { ThemeProvider } from '@material-ui/core/styles'; import { ApiProvider, ApiRegistry, diff --git a/packages/core-components/src/layout/InfoCard/InfoCard.stories.tsx b/packages/core-components/src/layout/InfoCard/InfoCard.stories.tsx index cf012a129a..8dd8272f5a 100644 --- a/packages/core-components/src/layout/InfoCard/InfoCard.stories.tsx +++ b/packages/core-components/src/layout/InfoCard/InfoCard.stories.tsx @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Grid, Typography } from '@material-ui/core'; + +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; import React, { PropsWithChildren } from 'react'; import { MemoryRouter } from 'react-router'; import { InfoCard } from './InfoCard'; diff --git a/packages/core-components/src/layout/InfoCard/InfoCard.tsx b/packages/core-components/src/layout/InfoCard/InfoCard.tsx index a3094b792e..0b9c4c7dcf 100644 --- a/packages/core-components/src/layout/InfoCard/InfoCard.tsx +++ b/packages/core-components/src/layout/InfoCard/InfoCard.tsx @@ -15,16 +15,12 @@ */ import React, { ReactNode } from 'react'; -import { - Card, - CardActions, - CardContent, - CardHeader, - CardHeaderProps, - Divider, - withStyles, - makeStyles, -} from '@material-ui/core'; +import { withStyles, makeStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardActions from '@material-ui/core/CardActions'; +import CardContent from '@material-ui/core/CardContent'; +import CardHeader, { CardHeaderProps } from '@material-ui/core/CardHeader'; +import Divider from '@material-ui/core/Divider'; import classNames from 'classnames'; import { ErrorBoundary, ErrorBoundaryProps } from '../ErrorBoundary'; import { BottomLink, BottomLinkProps } from '../BottomLink'; diff --git a/packages/core-components/src/layout/ItemCard/ItemCard.stories.tsx b/packages/core-components/src/layout/ItemCard/ItemCard.stories.tsx index 7cd67ff00d..1d336fc5f5 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCard.stories.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCard.stories.tsx @@ -14,14 +14,12 @@ * limitations under the License. */ -import { - Card, - CardActions, - CardContent, - CardMedia, - makeStyles, - Typography, -} from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardActions from '@material-ui/core/CardActions'; +import CardContent from '@material-ui/core/CardContent'; +import CardMedia from '@material-ui/core/CardMedia'; +import Typography from '@material-ui/core/Typography'; import React from 'react'; import { MemoryRouter } from 'react-router'; import { Button } from '../../components'; diff --git a/packages/core-components/src/layout/ItemCard/ItemCard.tsx b/packages/core-components/src/layout/ItemCard/ItemCard.tsx index 81a88d2c09..82426c14e1 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCard.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCard.tsx @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - Box, - Card, - CardActions, - CardContent, - CardMedia, - Chip, -} from '@material-ui/core'; + +import Box from '@material-ui/core/Box'; +import Card from '@material-ui/core/Card'; +import CardActions from '@material-ui/core/CardActions'; +import CardContent from '@material-ui/core/CardContent'; +import CardMedia from '@material-ui/core/CardMedia'; +import Chip from '@material-ui/core/Chip'; import React, { ReactNode } from 'react'; import { Button } from '../../components'; import { ItemCardHeader } from './ItemCardHeader'; diff --git a/packages/core-components/src/layout/ItemCard/ItemCardGrid.test.tsx b/packages/core-components/src/layout/ItemCard/ItemCardGrid.test.tsx index 30fdb415e5..f8759ffd17 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCardGrid.test.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCardGrid.test.tsx @@ -15,7 +15,7 @@ */ import { renderInTestApp } from '@backstage/test-utils'; -import { Card } from '@material-ui/core'; +import Card from '@material-ui/core/Card'; import { screen } from '@testing-library/react'; import React from 'react'; import { ItemCardGrid } from './ItemCardGrid'; diff --git a/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx b/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx index 739398252e..b4fe504c31 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx @@ -14,7 +14,12 @@ * limitations under the License. */ -import { createStyles, makeStyles, Theme, WithStyles } from '@material-ui/core'; +import { + createStyles, + makeStyles, + Theme, + WithStyles, +} from '@material-ui/core/styles'; import React from 'react'; export type ItemCardGridClassKey = 'root'; diff --git a/packages/core-components/src/layout/ItemCard/ItemCardHeader.test.tsx b/packages/core-components/src/layout/ItemCard/ItemCardHeader.test.tsx index 41b150a900..a401dfb4a5 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCardHeader.test.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCardHeader.test.tsx @@ -15,7 +15,8 @@ */ import { renderInTestApp } from '@backstage/test-utils'; -import { Card, CardMedia } from '@material-ui/core'; +import Card from '@material-ui/core/Card'; +import CardMedia from '@material-ui/core/CardMedia'; import { screen } from '@testing-library/react'; import React from 'react'; import { ItemCardHeader } from './ItemCardHeader'; diff --git a/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx b/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx index 7ad6732e02..c604f24e3f 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx @@ -14,12 +14,8 @@ * limitations under the License. */ -import { - createStyles, - makeStyles, - Typography, - WithStyles, -} from '@material-ui/core'; +import { createStyles, makeStyles, WithStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; import React from 'react'; import { BackstageTheme } from '@backstage/theme'; diff --git a/packages/core-components/src/layout/Page/Page.stories.tsx b/packages/core-components/src/layout/Page/Page.stories.tsx index 3ad9ecfe72..7386d198a3 100644 --- a/packages/core-components/src/layout/Page/Page.stories.tsx +++ b/packages/core-components/src/layout/Page/Page.stories.tsx @@ -14,7 +14,11 @@ * limitations under the License. */ -import { Box, Chip, Grid, Link, Typography } from '@material-ui/core'; +import Box from '@material-ui/core/Box'; +import Chip from '@material-ui/core/Chip'; +import Grid from '@material-ui/core/Grid'; +import Link from '@material-ui/core/Link'; +import Typography from '@material-ui/core/Typography'; import React, { useState } from 'react'; import { MemoryRouter } from 'react-router'; import { createApp } from '@backstage/core-app-api'; diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index 37d6c663f2..57653d23b5 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -16,7 +16,7 @@ import React, { PropsWithChildren } from 'react'; import { BackstageTheme } from '@backstage/theme'; -import { makeStyles, ThemeProvider } from '@material-ui/core'; +import { makeStyles, ThemeProvider } from '@material-ui/core/styles'; export type PageClassKey = 'root'; diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 750ec6bbdc..e6fa3820ad 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ -import { makeStyles, useMediaQuery } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; import clsx from 'clsx'; import React, { useRef, useState, useContext, PropsWithChildren } from 'react'; import { sidebarConfig, SidebarContext } from './config'; diff --git a/packages/core-components/src/layout/Sidebar/Intro.tsx b/packages/core-components/src/layout/Sidebar/Intro.tsx index 70b952588d..8086898e64 100644 --- a/packages/core-components/src/layout/Sidebar/Intro.tsx +++ b/packages/core-components/src/layout/Sidebar/Intro.tsx @@ -16,7 +16,10 @@ import React, { useContext, useState } from 'react'; import { useLocalStorage } from 'react-use'; -import { Link, Typography, makeStyles, Collapse } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Link from '@material-ui/core/Link'; +import Typography from '@material-ui/core/Typography'; +import Collapse from '@material-ui/core/Collapse'; import CloseIcon from '@material-ui/icons/Close'; import { BackstageTheme } from '@backstage/theme'; import { diff --git a/packages/core-components/src/layout/Sidebar/Items.test.tsx b/packages/core-components/src/layout/Sidebar/Items.test.tsx index 89d2a2c294..123c30ceae 100644 --- a/packages/core-components/src/layout/Sidebar/Items.test.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.test.tsx @@ -16,14 +16,14 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; -import { screen } from '@testing-library/react'; +import { createEvent, fireEvent, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import HomeIcon from '@material-ui/icons/Home'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; import { Sidebar } from './Bar'; -import { SidebarItem } from './Items'; +import { SidebarItem, SidebarSearchField } from './Items'; import { renderHook } from '@testing-library/react-hooks'; -import { hexToRgb, makeStyles } from '@material-ui/core'; +import { hexToRgb, makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles({ spotlight: { @@ -36,6 +36,7 @@ async function renderSidebar() { await renderInTestApp( + {}} to="/search" /> { ).toHaveStyle(`background-color: ${hexToRgb('2b2a2a')}`); }); }); + describe('SidebarSearchField', () => { + it('should be defaultPrevented when enter is pressed', async () => { + const searchEvent = createEvent.keyDown( + await screen.findByPlaceholderText('Search'), + { key: 'Enter', code: 'Enter', charCode: 13 }, + ); + fireEvent(await screen.findByPlaceholderText('Search'), searchEvent); + expect(searchEvent.defaultPrevented).toBeTruthy(); + }); + }); }); diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 8bd80b6825..883e0395cd 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -16,14 +16,10 @@ import { IconComponent } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; -import { - Badge, - makeStyles, - styled, - TextField, - Theme, - Typography, -} from '@material-ui/core'; +import { makeStyles, styled, Theme } from '@material-ui/core/styles'; +import Badge from '@material-ui/core/Badge'; +import TextField from '@material-ui/core/TextField'; +import Typography from '@material-ui/core/Typography'; import { CreateCSSProperties } from '@material-ui/core/styles/withStyles'; import SearchIcon from '@material-ui/icons/Search'; import clsx from 'clsx'; @@ -310,6 +306,7 @@ export function SidebarSearchField(props: SidebarSearchFieldProps) { const handleEnter: KeyboardEventHandler = ev => { if (ev.key === 'Enter') { + ev.preventDefault(); search(); } }; diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index c375112321..cc50c5af6b 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import React, { createContext, PropsWithChildren, diff --git a/packages/core-components/src/layout/SignInPage/SignInPage.tsx b/packages/core-components/src/layout/SignInPage/SignInPage.tsx index 910fc6a7bf..bd4fe37480 100644 --- a/packages/core-components/src/layout/SignInPage/SignInPage.tsx +++ b/packages/core-components/src/layout/SignInPage/SignInPage.tsx @@ -20,7 +20,9 @@ import { SignInPageProps, useApi, } from '@backstage/core-plugin-api'; -import { Button, Grid, Typography } from '@material-ui/core'; +import Button from '@material-ui/core/Button'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; import React, { useState } from 'react'; import { useMount } from 'react-use'; import { Progress } from '../../components/Progress'; diff --git a/packages/core-components/src/layout/SignInPage/auth0Provider.tsx b/packages/core-components/src/layout/SignInPage/auth0Provider.tsx index 78043cb3fb..19836827c9 100644 --- a/packages/core-components/src/layout/SignInPage/auth0Provider.tsx +++ b/packages/core-components/src/layout/SignInPage/auth0Provider.tsx @@ -15,7 +15,9 @@ */ import React from 'react'; -import { Grid, Typography, Button } from '@material-ui/core'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; import { InfoCard } from '../InfoCard/InfoCard'; import { ProviderComponent, ProviderLoader, SignInProvider } from './types'; import { diff --git a/packages/core-components/src/layout/SignInPage/commonProvider.tsx b/packages/core-components/src/layout/SignInPage/commonProvider.tsx index 3160bd171b..8df5faf34e 100644 --- a/packages/core-components/src/layout/SignInPage/commonProvider.tsx +++ b/packages/core-components/src/layout/SignInPage/commonProvider.tsx @@ -15,7 +15,8 @@ */ import React from 'react'; -import { Typography, Button } from '@material-ui/core'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; import { InfoCard } from '../InfoCard/InfoCard'; import { ProviderComponent, diff --git a/packages/core-components/src/layout/SignInPage/customProvider.tsx b/packages/core-components/src/layout/SignInPage/customProvider.tsx index 46fed6b5ae..3be375ed3d 100644 --- a/packages/core-components/src/layout/SignInPage/customProvider.tsx +++ b/packages/core-components/src/layout/SignInPage/customProvider.tsx @@ -16,14 +16,12 @@ import React from 'react'; import { useForm, UseFormRegisterReturn } from 'react-hook-form'; -import { - Typography, - Button, - FormControl, - TextField, - FormHelperText, - makeStyles, -} from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; +import FormControl from '@material-ui/core/FormControl'; +import TextField from '@material-ui/core/TextField'; +import FormHelperText from '@material-ui/core/FormHelperText'; import isEmpty from 'lodash/isEmpty'; import { InfoCard } from '../InfoCard/InfoCard'; import { ProviderComponent, ProviderLoader, SignInProvider } from './types'; diff --git a/packages/core-components/src/layout/SignInPage/guestProvider.tsx b/packages/core-components/src/layout/SignInPage/guestProvider.tsx index 8673c17ffa..e91d9cbc75 100644 --- a/packages/core-components/src/layout/SignInPage/guestProvider.tsx +++ b/packages/core-components/src/layout/SignInPage/guestProvider.tsx @@ -15,7 +15,8 @@ */ import React from 'react'; -import { Typography, Button } from '@material-ui/core'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; import { InfoCard } from '../InfoCard/InfoCard'; import { GridItem } from './styles'; import { ProviderComponent, ProviderLoader, SignInProvider } from './types'; diff --git a/packages/core-components/src/layout/SignInPage/styles.tsx b/packages/core-components/src/layout/SignInPage/styles.tsx index 55db36229b..6cf5ded226 100644 --- a/packages/core-components/src/layout/SignInPage/styles.tsx +++ b/packages/core-components/src/layout/SignInPage/styles.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ import React from 'react'; -import { Grid, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Grid from '@material-ui/core/Grid'; export type SignInPageClassKey = 'container' | 'item'; diff --git a/packages/core-components/src/layout/TabbedCard/TabbedCard.stories.tsx b/packages/core-components/src/layout/TabbedCard/TabbedCard.stories.tsx index 3214d26653..77fff64fb5 100644 --- a/packages/core-components/src/layout/TabbedCard/TabbedCard.stories.tsx +++ b/packages/core-components/src/layout/TabbedCard/TabbedCard.stories.tsx @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Grid } from '@material-ui/core'; + +import Grid from '@material-ui/core/Grid'; import React, { PropsWithChildren, useState } from 'react'; import { MemoryRouter } from 'react-router'; import { CardTab, TabbedCard } from './TabbedCard'; diff --git a/packages/core-components/src/layout/TabbedCard/TabbedCard.tsx b/packages/core-components/src/layout/TabbedCard/TabbedCard.tsx index e9905663b2..f9ec755e00 100644 --- a/packages/core-components/src/layout/TabbedCard/TabbedCard.tsx +++ b/packages/core-components/src/layout/TabbedCard/TabbedCard.tsx @@ -20,17 +20,13 @@ import React, { ReactNode, PropsWithChildren, } from 'react'; -import { - Card, - CardContent, - CardHeader, - Divider, - withStyles, - makeStyles, - Tabs, - Tab, - TabProps, -} from '@material-ui/core'; +import { withStyles, makeStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardContent from '@material-ui/core/CardContent'; +import CardHeader from '@material-ui/core/CardHeader'; +import Divider from '@material-ui/core/Divider'; +import Tabs from '@material-ui/core/Tabs'; +import Tab, { TabProps } from '@material-ui/core/Tab'; import { BottomLink, BottomLinkProps } from '../BottomLink'; import { ErrorBoundary, ErrorBoundaryProps } from '../ErrorBoundary'; diff --git a/packages/integration/src/azure/config.test.ts b/packages/integration/src/azure/config.test.ts index 385086fbcf..353065a030 100644 --- a/packages/integration/src/azure/config.test.ts +++ b/packages/integration/src/azure/config.test.ts @@ -31,15 +31,17 @@ describe('readAzureIntegrationConfig', () => { data: Partial, ): Promise { const fullSchema = await loadConfigSchema({ - dependencies: [require('../../package.json').name], + dependencies: ['@backstage/integration'], }); const serializedSchema = fullSchema.serialize() as { - schemas: { path: string }[]; + schemas: { value: { properties?: { integrations?: object } } }[]; }; const schema = await loadConfigSchema({ serialized: { - ...serializedSchema, // grab the schema from this package only - schemas: serializedSchema.schemas.filter(s => s.path === 'config.d.ts'), + ...serializedSchema, // only include schemas that apply to integrations + schemas: serializedSchema.schemas.filter( + s => s.value?.properties?.integrations, + ), }, }); const processed = schema.process( diff --git a/packages/integration/src/bitbucket/config.test.ts b/packages/integration/src/bitbucket/config.test.ts index b887566e35..5440b174c8 100644 --- a/packages/integration/src/bitbucket/config.test.ts +++ b/packages/integration/src/bitbucket/config.test.ts @@ -31,15 +31,17 @@ describe('readBitbucketIntegrationConfig', () => { data: Partial, ): Promise { const fullSchema = await loadConfigSchema({ - dependencies: [require('../../package.json').name], + dependencies: ['@backstage/integration'], }); const serializedSchema = fullSchema.serialize() as { - schemas: { path: string }[]; + schemas: { value: { properties?: { integrations?: object } } }[]; }; const schema = await loadConfigSchema({ serialized: { - ...serializedSchema, // grab the schema from this package only - schemas: serializedSchema.schemas.filter(s => s.path === 'config.d.ts'), + ...serializedSchema, // only include schemas that apply to integrations + schemas: serializedSchema.schemas.filter( + s => s.value?.properties?.integrations, + ), }, }); const processed = schema.process( diff --git a/packages/integration/src/github/config.test.ts b/packages/integration/src/github/config.test.ts index eb084b14a9..73efb4f81a 100644 --- a/packages/integration/src/github/config.test.ts +++ b/packages/integration/src/github/config.test.ts @@ -31,15 +31,17 @@ describe('readGitHubIntegrationConfig', () => { data: Partial, ): Promise { const fullSchema = await loadConfigSchema({ - dependencies: [require('../../package.json').name], + dependencies: ['@backstage/integration'], }); const serializedSchema = fullSchema.serialize() as { - schemas: { path: string }[]; + schemas: { value: { properties?: { integrations?: object } } }[]; }; const schema = await loadConfigSchema({ serialized: { - ...serializedSchema, // grab the schema from this package only - schemas: serializedSchema.schemas.filter(s => s.path === 'config.d.ts'), + ...serializedSchema, // only include schemas that apply to integrations + schemas: serializedSchema.schemas.filter( + s => s.value?.properties?.integrations, + ), }, }); const processed = schema.process( diff --git a/packages/integration/src/gitlab/config.test.ts b/packages/integration/src/gitlab/config.test.ts index 1308704e9d..db03285206 100644 --- a/packages/integration/src/gitlab/config.test.ts +++ b/packages/integration/src/gitlab/config.test.ts @@ -31,15 +31,17 @@ describe('readGitLabIntegrationConfig', () => { data: Partial, ): Promise { const fullSchema = await loadConfigSchema({ - dependencies: [require('../../package.json').name], + dependencies: ['@backstage/integration'], }); const serializedSchema = fullSchema.serialize() as { - schemas: { path: string }[]; + schemas: { value: { properties?: { integrations?: object } } }[]; }; const schema = await loadConfigSchema({ serialized: { - ...serializedSchema, // grab the schema from this package only - schemas: serializedSchema.schemas.filter(s => s.path === 'config.d.ts'), + ...serializedSchema, // only include schemas that apply to integrations + schemas: serializedSchema.schemas.filter( + s => s.value?.properties?.integrations, + ), }, }); const processed = schema.process( diff --git a/packages/theme/src/baseTheme.ts b/packages/theme/src/baseTheme.ts index b2d964c4db..4a45516a55 100644 --- a/packages/theme/src/baseTheme.ts +++ b/packages/theme/src/baseTheme.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createTheme as createMuiTheme } from '@material-ui/core'; +import { createTheme as createMuiTheme } from '@material-ui/core/styles'; import { darken, lighten } from '@material-ui/core/styles/colorManipulator'; import { Overrides } from '@material-ui/core/styles/overrides'; diff --git a/plugins/api-docs/dev/index.tsx b/plugins/api-docs/dev/index.tsx index e60647e7b9..633a285b98 100644 --- a/plugins/api-docs/dev/index.tsx +++ b/plugins/api-docs/dev/index.tsx @@ -15,29 +15,34 @@ */ import { ApiEntity, Entity } from '@backstage/catalog-model'; +import { Content, Header, Page } from '@backstage/core-components'; import { createDevApp } from '@backstage/dev-utils'; +import { CatalogEntityPage } from '@backstage/plugin-catalog'; import { catalogApiRef, EntityProvider } from '@backstage/plugin-catalog-react'; import React from 'react'; import { apiDocsConfigRef, + apiDocsPlugin, ApiExplorerPage, defaultDefinitionWidgets, EntityApiDefinitionCard, } from '../src'; import asyncapiApiEntity from './asyncapi-example-api.yaml'; import graphqlApiEntity from './graphql-example-api.yaml'; +import invalidLanguageApiEntity from './invalid-language-example-api.yaml'; import openapiApiEntity from './openapi-example-api.yaml'; import otherApiEntity from './other-example-api.yaml'; -import { Content, Header, Page } from '@backstage/core-components'; const mockEntities = [ openapiApiEntity, asyncapiApiEntity, graphqlApiEntity, + invalidLanguageApiEntity, otherApiEntity, ] as unknown as Entity[]; createDevApp() + .registerPlugin(apiDocsPlugin) .registerApi({ api: catalogApiRef, deps: {}, @@ -65,6 +70,7 @@ createDevApp() }; }, }) + .addPage({ element: }) .addPage({ title: 'API Explorer', element: }) .addPage({ title: 'OpenAPI', @@ -105,6 +111,19 @@ createDevApp() ), }) + .addPage({ + title: 'Invalid Language', + element: ( + +
+ + + + + + + ), + }) .addPage({ title: 'Other', element: ( diff --git a/plugins/api-docs/dev/invalid-language-example-api.yaml b/plugins/api-docs/dev/invalid-language-example-api.yaml new file mode 100644 index 0000000000..01b8ae9457 --- /dev/null +++ b/plugins/api-docs/dev/invalid-language-example-api.yaml @@ -0,0 +1,48 @@ +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: hello-world + description: Hello World example for gRPC +spec: + type: my-invalid-language + lifecycle: deprecated + owner: team-c + definition: | + // Copyright 2015 gRPC 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. + + syntax = "proto3"; + + option java_multiple_files = true; + option java_package = "io.grpc.examples.helloworld"; + option java_outer_classname = "HelloWorldProto"; + option objc_class_prefix = "HLW"; + + package helloworld; + + // The greeting service definition. + service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} + } + + // The request message containing the user's name. + message HelloRequest { + string name = 1; + } + + // The response message containing the greetings + message HelloReply { + string message = 1; + } diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index be3fdf7229..9a89837102 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -49,7 +49,7 @@ "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-use": "^17.2.4", - "swagger-ui-react": "^3.37.2" + "swagger-ui-react": "^4.0.0-rc.3" }, "devDependencies": { "@backstage/cli": "^0.7.15", diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index 0b9f0eb8c4..0bdd73f3e8 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -3,6 +3,8 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +/// + import { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; import { Entity } from '@backstage/catalog-model'; @@ -32,7 +34,6 @@ export type AuthProviderFactoryOptions = { tokenIssuer: TokenIssuer; discovery: PluginEndpointDiscovery; catalogApi: CatalogApi; - identityResolver?: ExperimentalIdentityResolver; }; // Warning: (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag @@ -74,6 +75,16 @@ export type AuthResponse = { backstageIdentity?: BackstageIdentity; }; +// Warning: (ae-missing-release-tag) "AwsAlbProviderOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type AwsAlbProviderOptions = { + authHandler?: AuthHandler; + signIn: { + resolver: SignInResolver; + }; +}; + // Warning: (ae-missing-release-tag) "BackstageIdentity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -135,6 +146,13 @@ export const bitbucketUserIdSignInResolver: SignInResolver // @public (undocumented) export const bitbucketUsernameSignInResolver: SignInResolver; +// Warning: (ae-missing-release-tag) "createAwsAlbProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const createAwsAlbProvider: ( + options?: AwsAlbProviderOptions | undefined, +) => AuthProviderFactory; + // Warning: (ae-missing-release-tag) "createBitbucketProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -526,9 +544,9 @@ export type WebMessageResponse = // // src/identity/types.d.ts:25:5 - (ae-forgotten-export) The symbol "TokenParams" needs to be exported by the entry point index.d.ts // src/identity/types.d.ts:31:9 - (ae-forgotten-export) The symbol "AnyJWK" needs to be exported by the entry point index.d.ts -// src/providers/bitbucket/provider.d.ts:61:5 - (ae-forgotten-export) The symbol "AuthHandler" needs to be exported by the entry point index.d.ts -// src/providers/bitbucket/provider.d.ts:69:9 - (ae-forgotten-export) The symbol "SignInResolver" needs to be exported by the entry point index.d.ts -// src/providers/types.d.ts:109:5 - (ae-forgotten-export) The symbol "AuthProviderConfig" needs to be exported by the entry point index.d.ts -// src/providers/types.d.ts:115:5 - (ae-forgotten-export) The symbol "ExperimentalIdentityResolver" needs to be exported by the entry point index.d.ts -// src/providers/types.d.ts:132:8 - (tsdoc-missing-deprecation-message) The @deprecated block must include a deprecation message, e.g. describing the recommended alternative +// src/providers/aws-alb/provider.d.ts:77:5 - (ae-forgotten-export) The symbol "AuthHandler" needs to be exported by the entry point index.d.ts +// src/providers/aws-alb/provider.d.ts:77:5 - (ae-forgotten-export) The symbol "AwsAlbResult" needs to be exported by the entry point index.d.ts +// src/providers/aws-alb/provider.d.ts:85:9 - (ae-forgotten-export) The symbol "SignInResolver" needs to be exported by the entry point index.d.ts +// src/providers/types.d.ts:99:5 - (ae-forgotten-export) The symbol "AuthProviderConfig" needs to be exported by the entry point index.d.ts +// src/providers/types.d.ts:121:8 - (tsdoc-missing-deprecation-message) The @deprecated block must include a deprecation message, e.g. describing the recommended alternative ``` diff --git a/plugins/auth-backend/src/providers/aws-alb/provider.test.ts b/plugins/auth-backend/src/providers/aws-alb/provider.test.ts index ca4e521316..2f5ffa1833 100644 --- a/plugins/auth-backend/src/providers/aws-alb/provider.test.ts +++ b/plugins/auth-backend/src/providers/aws-alb/provider.test.ts @@ -17,8 +17,14 @@ import { getVoidLogger } from '@backstage/backend-common'; import express from 'express'; import { JWT } from 'jose'; -import { AwsAlbAuthProvider } from './provider'; -import { AuthResponse } from '../types'; +import { + ALB_ACCESSTOKEN_HEADER, + ALB_JWT_HEADER, + AwsAlbAuthProvider, +} from './provider'; +import { TokenIssuer } from '../../identity/types'; +import { CatalogIdentityClient } from '../../lib/catalog'; +import { makeProfileInfo } from '../../lib/passport'; const jwtMock = JWT as jest.Mocked; @@ -29,9 +35,21 @@ yOlxJ2VW88mLAQGJ7HPAvOdylxZsItMnzCuqNzZvie8m/NJsOjhDncVkrw== -----END PUBLIC KEY----- `; }; +const mockJwt = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IktFWV9JRCIsImlzcyI6IklTU1VFUl9VUkwifQ.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlVzZXIgTmFtZSIsImlhdCI6MTUxNjIzOTAyMn0.uMCSBGhij1xn5pnot8XgD-huQuTIBOFGs6kkW_p_X94'; +const mockAccessToken = 'ACCESS_TOKEN'; +const mockClaims = { + sub: '1234567890', + name: 'User Name', + family_name: 'Name', + given_name: 'User', + picture: 'PICTURE_URL', + email: 'user.name@email.test', + exp: 1632833763, + iss: 'ISSUER_URL', +}; jest.mock('jose'); - jest.mock('cross-fetch', () => ({ __esModule: true, default: async () => { @@ -43,53 +61,48 @@ jest.mock('cross-fetch', () => ({ }, })); -const identityResolutionCallbackMock = async (): Promise> => { - return { - backstageIdentity: { - id: 'foo', - idToken: '', - }, - profile: { - displayName: 'Foo Bar', - }, - providerInfo: {}, - }; -}; - -const identityResolutionCallbackRejectedMock = async (): Promise< - AuthResponse -> => { - throw new Error('failed'); -}; - beforeEach(() => { jest.clearAllMocks(); }); -describe('AwsALBAuthProvider', () => { - const catalogApi = { - addLocation: jest.fn(), - removeLocationById: jest.fn(), - getEntities: jest.fn(), - getOriginLocationByEntity: jest.fn(), - getLocationByEntity: jest.fn(), - getLocationById: jest.fn(), - removeEntityByUid: jest.fn(), - getEntityByName: jest.fn(), - refreshEntity: jest.fn(), - getEntityAncestors: jest.fn(), +describe('AwsAlbAuthProvider', () => { + const tokenIssuer: TokenIssuer = { + listPublicKeys: jest.fn(), + async issueToken(params) { + return `token-for-${params.claims.sub}`; + }, }; + const catalogIdentityClient: CatalogIdentityClient = { + findUser: jest.fn(), + } as unknown as CatalogIdentityClient; const mockRequest = { - header: jest.fn(() => { - return 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyIsImlzcyI6ImZvbyJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.T2BNS4G-6RoiFnXc8Q8TiwdWzTpNitY8jcsGM3N3-Yo'; - }), - } as unknown as express.Request; - const mockRequestWithoutJwt = { - header: jest.fn(() => { + header: jest.fn(name => { + if (name === ALB_JWT_HEADER) { + return mockJwt; + } else if (name === ALB_ACCESSTOKEN_HEADER) { + return mockAccessToken; + } return undefined; }), } as unknown as express.Request; + const mockRequestWithoutJwt = { + header: jest.fn(name => { + if (name === ALB_ACCESSTOKEN_HEADER) { + return mockAccessToken; + } + return undefined; + }), + } as unknown as express.Request; + const mockRequestWithoutAccessToken = { + header: jest.fn(name => { + if (name === ALB_JWT_HEADER) { + return mockJwt; + } + return undefined; + }), + } as unknown as express.Request; + const mockResponse = { end: jest.fn(), header: () => jest.fn(), @@ -97,38 +110,78 @@ describe('AwsALBAuthProvider', () => { status: jest.fn(), } as unknown as express.Response; - describe('should transform to type OAuthResponse', () => { + describe('should transform to type AwsAlbResponse', () => { it('when JWT is valid and identity is resolved successfully', async () => { - const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogApi, { - region: 'us-west-2', - identityResolutionCallback: identityResolutionCallbackMock, - issuer: 'foo', + const provider = new AwsAlbAuthProvider({ + region: 'eu-west-1', + issuer: 'ISSUER_URL', + logger: getVoidLogger(), + catalogIdentityClient, + tokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }), + signInResolver: async () => { + return { id: 'user.name', token: 'TOKEN' }; + }, }); - jwtMock.verify.mockImplementationOnce(() => ({ - sub: 'foo', - })); + jwtMock.verify.mockReturnValueOnce(mockClaims); await provider.refresh(mockRequest, mockResponse); expect(mockResponse.json).toHaveBeenCalledWith({ backstageIdentity: { - id: 'foo', - idToken: '', + id: 'user.name', + token: 'TOKEN', }, profile: { - displayName: 'Foo Bar', + displayName: 'User Name', + email: 'user.name@email.test', + picture: 'PICTURE_URL', + }, + providerInfo: { + accessToken: mockAccessToken, + expiresInSeconds: mockClaims.exp, }, - providerInfo: {}, }); }); }); + describe('should fail when', () => { + it('Access token is missing', async () => { + const provider = new AwsAlbAuthProvider({ + region: 'eu-west-1', + issuer: 'ISSUER_URL', + logger: getVoidLogger(), + catalogIdentityClient, + tokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }), + signInResolver: async () => { + return { id: 'user.name', token: 'TOKEN' }; + }, + }); + + await provider.refresh(mockRequestWithoutAccessToken, mockResponse); + + expect(mockResponse.status).toHaveBeenCalledWith(401); + }); + it('JWT is missing', async () => { - const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogApi, { - region: 'us-west-2', - identityResolutionCallback: identityResolutionCallbackMock, - issuer: 'foo', + const provider = new AwsAlbAuthProvider({ + region: 'eu-west-1', + issuer: 'ISSUER_URL', + logger: getVoidLogger(), + catalogIdentityClient, + tokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }), + signInResolver: async () => { + return { id: 'user.name', token: 'TOKEN' }; + }, }); await provider.refresh(mockRequestWithoutJwt, mockResponse); @@ -137,10 +190,18 @@ describe('AwsALBAuthProvider', () => { }); it('JWT is invalid', async () => { - const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogApi, { - region: 'us-west-2', - identityResolutionCallback: identityResolutionCallbackMock, - issuer: 'foo', + const provider = new AwsAlbAuthProvider({ + region: 'eu-west-1', + issuer: 'ISSUER_URL', + logger: getVoidLogger(), + catalogIdentityClient, + tokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }), + signInResolver: async () => { + return { id: 'user.name', token: 'TOKEN' }; + }, }); jwtMock.verify.mockImplementationOnce(() => { @@ -152,11 +213,19 @@ describe('AwsALBAuthProvider', () => { expect(mockResponse.status).toHaveBeenCalledWith(401); }); - it('issuer is invalid', async () => { - const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogApi, { - region: 'us-west-2', - identityResolutionCallback: identityResolutionCallbackMock, - issuer: 'foobar', + it('issuer is missing', async () => { + const provider = new AwsAlbAuthProvider({ + region: 'eu-west-1', + issuer: 'ISSUER_URL', + logger: getVoidLogger(), + catalogIdentityClient, + tokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }), + signInResolver: async () => { + return { id: 'user.name', token: 'TOKEN' }; + }, }); jwtMock.verify.mockReturnValueOnce({}); @@ -165,14 +234,68 @@ describe('AwsALBAuthProvider', () => { expect(mockResponse.status).toHaveBeenCalledWith(401); }); - it('identity resolution callback rejects', async () => { - const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogApi, { - region: 'us-west-2', - identityResolutionCallback: identityResolutionCallbackRejectedMock, - issuer: 'foo', + it('issuer is invalid', async () => { + const provider = new AwsAlbAuthProvider({ + region: 'eu-west-1', + issuer: 'ISSUER_URL', + logger: getVoidLogger(), + catalogIdentityClient, + tokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }), + signInResolver: async () => { + return { id: 'user.name', token: 'TOKEN' }; + }, }); - jwtMock.verify.mockReturnValueOnce({}); + jwtMock.verify.mockReturnValueOnce({ + iss: 'INVALID_ISSUE_URL', + }); + + await provider.refresh(mockRequest, mockResponse); + expect(mockResponse.status).toHaveBeenCalledWith(401); + }); + + it('SignInResolver rejects', async () => { + const provider = new AwsAlbAuthProvider({ + region: 'eu-west-1', + issuer: 'ISSUER_URL', + logger: getVoidLogger(), + catalogIdentityClient, + tokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }), + signInResolver: async () => { + throw new Error(); + }, + }); + + jwtMock.verify.mockReturnValueOnce(mockClaims); + + await provider.refresh(mockRequest, mockResponse); + + expect(mockResponse.status).toHaveBeenCalledWith(401); + expect(mockResponse.end).toHaveBeenCalledTimes(1); + }); + + it('AuthHandler rejects', async () => { + const provider = new AwsAlbAuthProvider({ + region: 'eu-west-1', + issuer: 'ISSUER_URL', + logger: getVoidLogger(), + catalogIdentityClient, + tokenIssuer, + authHandler: async () => { + throw new Error(); + }, + signInResolver: async () => { + return { id: 'user.name', token: 'TOKEN' }; + }, + }); + + jwtMock.verify.mockReturnValueOnce(mockClaims); await provider.refresh(mockRequest, mockResponse); diff --git a/plugins/auth-backend/src/providers/aws-alb/provider.ts b/plugins/auth-backend/src/providers/aws-alb/provider.ts index bd16ae1960..ac5db5e69f 100644 --- a/plugins/auth-backend/src/providers/aws-alb/provider.ts +++ b/plugins/auth-backend/src/providers/aws-alb/provider.ts @@ -14,9 +14,11 @@ * limitations under the License. */ import { - AuthProviderFactoryOptions, + AuthHandler, + AuthProviderFactory, AuthProviderRouteHandlers, - ExperimentalIdentityResolver, + AuthResponse, + SignInResolver, } from '../types'; import express from 'express'; import fetch from 'cross-fetch'; @@ -25,66 +27,101 @@ import { KeyObject } from 'crypto'; import { Logger } from 'winston'; import NodeCache from 'node-cache'; import { JWT } from 'jose'; -import { CatalogApi } from '@backstage/catalog-client'; +import { TokenIssuer } from '../../identity/types'; +import { CatalogIdentityClient } from '../../lib/catalog'; +import { Profile as PassportProfile } from 'passport'; +import { makeProfileInfo } from '../../lib/passport'; +import { AuthenticationError } from '@backstage/errors'; -const ALB_JWT_HEADER = 'x-amzn-oidc-data'; -/** - * A callback function that receives a verified JWT and returns a UserEntity - * @param {payload} The verified JWT payload - */ -type AwsAlbAuthProviderOptions = { +export const ALB_JWT_HEADER = 'x-amzn-oidc-data'; +export const ALB_ACCESSTOKEN_HEADER = 'x-amzn-oidc-accesstoken'; + +type Options = { region: string; issuer?: string; - identityResolutionCallback: ExperimentalIdentityResolver; + logger: Logger; + authHandler: AuthHandler; + signInResolver: SignInResolver; + tokenIssuer: TokenIssuer; + catalogIdentityClient: CatalogIdentityClient; }; -export const getJWTHeaders = (input: string) => { + +export const getJWTHeaders = (input: string): AwsAlbHeaders => { const encoded = input.split('.')[0]; return JSON.parse(Buffer.from(encoded, 'base64').toString('utf8')); }; -export class AwsAlbAuthProvider implements AuthProviderRouteHandlers { - private logger: Logger; - private readonly catalogClient: CatalogApi; - private options: AwsAlbAuthProviderOptions; - private readonly keyCache: NodeCache; +export type AwsAlbHeaders = { + alg: string; + kid: string; + signer: string; + iss: string; + client: string; + exp: number; +}; - constructor( - logger: Logger, - catalogClient: CatalogApi, - options: AwsAlbAuthProviderOptions, - ) { - this.logger = logger; - this.catalogClient = catalogClient; - this.options = options; +export type AwsAlbClaims = { + sub: string; + name: string; + family_name: string; + given_name: string; + picture: string; + email: string; + exp: number; + iss: string; +}; + +export type AwsAlbResult = { + fullProfile: PassportProfile; + expiresInSeconds?: number; + accessToken: string; +}; + +export type AwsAlbProviderInfo = { + /** + * An access token issued for the signed in user. + */ + accessToken: string; + /** + * Expiry of the access token in seconds. + */ + expiresInSeconds?: number; +}; + +export type AwsAlbResponse = AuthResponse; + +export class AwsAlbAuthProvider implements AuthProviderRouteHandlers { + private readonly region: string; + private readonly issuer?: string; + private readonly tokenIssuer: TokenIssuer; + private readonly catalogIdentityClient: CatalogIdentityClient; + private readonly logger: Logger; + private readonly keyCache: NodeCache; + private readonly authHandler: AuthHandler; + private readonly signInResolver: SignInResolver; + + constructor(options: Options) { + this.region = options.region; + this.issuer = options.issuer; + this.authHandler = options.authHandler; + this.signInResolver = options.signInResolver; + this.tokenIssuer = options.tokenIssuer; + this.catalogIdentityClient = options.catalogIdentityClient; + this.logger = options.logger; this.keyCache = new NodeCache({ stdTTL: 3600 }); } + frameHandler(): Promise { return Promise.resolve(undefined); } async refresh(req: express.Request, res: express.Response): Promise { - const jwt = req.header(ALB_JWT_HEADER); - if (jwt !== undefined) { - try { - const headers = getJWTHeaders(jwt); - const key = await this.getKey(headers.kid); - const payload = JWT.verify(jwt, key); - - if (this.options.issuer && headers.iss !== this.options.issuer) { - throw new Error('issuer mismatch on JWT'); - } - - const resolvedEntity = await this.options.identityResolutionCallback( - payload, - this.catalogClient, - ); - res.json(resolvedEntity); - } catch (e) { - this.logger.error('exception occurred during JWT processing', e); - res.status(401); - res.end(); - } - } else { + try { + const result = await this.getResult(req); + const response = await this.handleResult(result); + res.json(response); + } catch (e) { + this.logger.error('Exception occurred during AWS ALB token refresh', e); res.status(401); res.end(); } @@ -94,13 +131,85 @@ export class AwsAlbAuthProvider implements AuthProviderRouteHandlers { return Promise.resolve(undefined); } + private async getResult(req: express.Request): Promise { + const jwt = req.header(ALB_JWT_HEADER); + const accessToken = req.header(ALB_ACCESSTOKEN_HEADER); + + if (jwt === undefined) { + throw new AuthenticationError( + `Missing ALB OIDC header: ${ALB_JWT_HEADER}`, + ); + } + + if (accessToken === undefined) { + throw new AuthenticationError( + `Missing ALB OIDC header: ${ALB_ACCESSTOKEN_HEADER}`, + ); + } + + try { + const headers = getJWTHeaders(jwt); + const key = await this.getKey(headers.kid); + const claims = JWT.verify(jwt, key) as AwsAlbClaims; + + if (this.issuer && claims.iss !== this.issuer) { + throw new AuthenticationError('Issuer mismatch on JWT token'); + } + + const fullProfile: PassportProfile = { + provider: 'unknown', + id: claims.sub, + displayName: claims.name, + username: claims.email.split('@')[0].toLowerCase(), + name: { + familyName: claims.family_name, + givenName: claims.given_name, + }, + emails: [{ value: claims.email.toLowerCase() }], + photos: [{ value: claims.picture }], + }; + + return { + fullProfile, + expiresInSeconds: claims.exp, + accessToken, + }; + } catch (e) { + throw new Error(`Exception occurred during JWT processing: ${e}`); + } + } + + private async handleResult(result: AwsAlbResult): Promise { + const { profile } = await this.authHandler(result); + const backstageIdentity = await this.signInResolver( + { + result, + profile, + }, + { + tokenIssuer: this.tokenIssuer, + catalogIdentityClient: this.catalogIdentityClient, + logger: this.logger, + }, + ); + + return { + providerInfo: { + accessToken: result.accessToken, + expiresInSeconds: result.expiresInSeconds, + }, + backstageIdentity, + profile, + }; + } + async getKey(keyId: string): Promise { const optionalCacheKey = this.keyCache.get(keyId); if (optionalCacheKey) { return crypto.createPublicKey(optionalCacheKey); } const keyText: string = await fetch( - `https://public-keys.auth.elb.${this.options.region}.amazonaws.com/${keyId}`, + `https://public-keys.auth.elb.${this.region}.amazonaws.com/${keyId}`, ).then(response => response.text()); const keyValue = crypto.createPublicKey(keyText); this.keyCache.set(keyId, keyValue.export({ format: 'pem', type: 'spki' })); @@ -108,26 +217,58 @@ export class AwsAlbAuthProvider implements AuthProviderRouteHandlers { } } -export type AwsAlbProviderOptions = {}; +export type AwsAlbProviderOptions = { + /** + * The profile transformation function used to verify and convert the auth response + * into the profile that will be presented to the user. + */ + authHandler?: AuthHandler; -export const createAwsAlbProvider = (_options?: AwsAlbProviderOptions) => { - return ({ - logger, - catalogApi, - config, - identityResolver, - }: AuthProviderFactoryOptions) => { - const region = config.getString('region'); - const issuer = config.getOptionalString('iss'); - if (identityResolver !== undefined) { - return new AwsAlbAuthProvider(logger, catalogApi, { - region, - issuer, - identityResolutionCallback: identityResolver, - }); - } - throw new Error( - 'Identity resolver is required to use this authentication provider', - ); + /** + * Configure sign-in for this provider, without it the provider can not be used to sign users in. + */ + signIn: { + /** + * Maps an auth result to a Backstage identity for the user. + */ + resolver: SignInResolver; + }; +}; + +export const createAwsAlbProvider = ( + options?: AwsAlbProviderOptions, +): AuthProviderFactory => { + return ({ config, tokenIssuer, catalogApi, logger }) => { + const region = config.getString('region'); + const issuer = config.getOptionalString('iss'); + + if (options?.signIn.resolver === undefined) { + throw new Error( + 'SignInResolver is required to use this authentication provider', + ); + } + + const catalogIdentityClient = new CatalogIdentityClient({ + catalogApi, + tokenIssuer, + }); + + const authHandler: AuthHandler = options?.authHandler + ? options.authHandler + : async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }); + + const signInResolver = options?.signIn.resolver; + + return new AwsAlbAuthProvider({ + region, + issuer, + signInResolver, + authHandler, + tokenIssuer, + catalogIdentityClient, + logger, + }); }; }; diff --git a/plugins/auth-backend/src/providers/index.ts b/plugins/auth-backend/src/providers/index.ts index 8c5f05fefd..4a9221ce21 100644 --- a/plugins/auth-backend/src/providers/index.ts +++ b/plugins/auth-backend/src/providers/index.ts @@ -22,6 +22,7 @@ export * from './oauth2'; export * from './okta'; export * from './bitbucket'; export * from './atlassian'; +export * from './aws-alb'; export { factories as defaultAuthProviderFactories } from './factories'; diff --git a/plugins/auth-backend/src/providers/types.ts b/plugins/auth-backend/src/providers/types.ts index 6f6df5d904..7f7b841c68 100644 --- a/plugins/auth-backend/src/providers/types.ts +++ b/plugins/auth-backend/src/providers/types.ts @@ -119,19 +119,6 @@ export interface AuthProviderRouteHandlers { logout?(req: express.Request, res: express.Response): Promise; } -/** - * EXPERIMENTAL - this will almost certainly break in a future release. - * - * Used to resolve an identity from auth information in some auth providers. - */ -export type ExperimentalIdentityResolver = ( - /** - * An object containing information specific to the auth provider. - */ - payload: object, - catalogApi: CatalogApi, -) => Promise>; - export type AuthProviderFactoryOptions = { providerId: string; globalConfig: AuthProviderConfig; @@ -140,7 +127,6 @@ export type AuthProviderFactoryOptions = { tokenIssuer: TokenIssuer; discovery: PluginEndpointDiscovery; catalogApi: CatalogApi; - identityResolver?: ExperimentalIdentityResolver; }; export type AuthProviderFactory = ( diff --git a/plugins/azure-devops-backend/api-report.md b/plugins/azure-devops-backend/api-report.md index 4ad2f833d7..74b4ac5b23 100644 --- a/plugins/azure-devops-backend/api-report.md +++ b/plugins/azure-devops-backend/api-report.md @@ -10,6 +10,7 @@ import { Config } from '@backstage/config'; import express from 'express'; import { GitRepository } from 'azure-devops-node-api/interfaces/GitInterfaces'; import { Logger as Logger_2 } from 'winston'; +import { PullRequestStatus } from 'azure-devops-node-api/interfaces/GitInterfaces'; import { WebApi } from 'azure-devops-node-api'; // Warning: (ae-missing-release-tag) "AzureDevOpsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -28,6 +29,14 @@ export class AzureDevOpsApi { projectName: string, repoName: string, ): Promise; + // Warning: (ae-forgotten-export) The symbol "PullRequestOptions" needs to be exported by the entry point index.d.ts + // + // (undocumented) + getPullRequests( + projectName: string, + repoName: string, + options: PullRequestOptions, + ): Promise; // (undocumented) getRepoBuilds( projectName: string, @@ -41,6 +50,23 @@ export class AzureDevOpsApi { // @public (undocumented) export function createRouter(options: RouterOptions): Promise; +// Warning: (ae-missing-release-tag) "PullRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PullRequest = { + pullRequestId?: number; + repoName?: string; + title?: string; + uniqueName?: string; + createdBy?: string; + creationDate?: Date; + sourceRefName?: string; + targetRefName?: string; + status?: PullRequestStatus; + isDraft?: boolean; + link: string; +}; + // Warning: (ae-missing-release-tag) "RepoBuild" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts index 25022b4c02..fd9aa169d5 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts @@ -13,17 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { repoBuildFromBuild } from './AzureDevOpsApi'; -import { RepoBuild } from './types'; +import { mappedPullRequest, mappedRepoBuild } from './AzureDevOpsApi'; +import { PullRequest, RepoBuild } from './types'; import { Build, BuildResult, BuildStatus, DefinitionReference, } from 'azure-devops-node-api/interfaces/BuildInterfaces'; +import { + GitPullRequest, + PullRequestStatus, +} from 'azure-devops-node-api/interfaces/GitInterfaces'; +import { GitRepository } from 'azure-devops-node-api/interfaces/TfvcInterfaces'; +import { IdentityRef } from 'azure-devops-node-api/interfaces/common/VSSInterfaces'; describe('AzureDevOpsApi', () => { - describe('repoBuildFromBuild', () => { + describe('mappedRepoBuild', () => { it('should return RepoBuild from Build', () => { const inputBuildDefinition: DefinitionReference = { name: 'My Build Definition', @@ -57,11 +63,11 @@ describe('AzureDevOpsApi', () => { source: 'refs/heads/develop (f4f78b31)', }; - expect(repoBuildFromBuild(inputBuild)).toEqual(outputRepoBuild); + expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); }); }); - describe('repoBuildFromBuild with no Build definition name', () => { + describe('mappedRepoBuild with no Build definition name', () => { it('should return RepoBuild with only Build Number for title', () => { const inputLinks: any = { web: { @@ -91,11 +97,11 @@ describe('AzureDevOpsApi', () => { source: 'refs/heads/develop (f4f78b31)', }; - expect(repoBuildFromBuild(inputBuild)).toEqual(outputRepoBuild); + expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); }); }); - describe('repoBuildFromBuild with undefined status', () => { + describe('mappedRepoBuild with undefined status', () => { it('should return BuildStatus of None for status', () => { const inputLinks: any = { web: { @@ -125,11 +131,11 @@ describe('AzureDevOpsApi', () => { source: 'refs/heads/develop (f4f78b31)', }; - expect(repoBuildFromBuild(inputBuild)).toEqual(outputRepoBuild); + expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); }); }); - describe('repoBuildFromBuild with undefined result', () => { + describe('mappedRepoBuild with undefined result', () => { it('should return BuildResult of None for result', () => { const inputLinks: any = { web: { @@ -159,11 +165,11 @@ describe('AzureDevOpsApi', () => { source: 'refs/heads/develop (f4f78b31)', }; - expect(repoBuildFromBuild(inputBuild)).toEqual(outputRepoBuild); + expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); }); }); - describe('repoBuildFromBuild with undefined link', () => { + describe('mappedRepoBuild with undefined link', () => { it('should return empty string for link', () => { const inputBuild: Build = { id: 1, @@ -187,7 +193,53 @@ describe('AzureDevOpsApi', () => { source: 'refs/heads/develop (f4f78b31)', }; - expect(repoBuildFromBuild(inputBuild)).toEqual(outputRepoBuild); + expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); + }); + }); + + describe('mappedPullRequest', () => { + it('should return PullRequest from GitPullRequest', () => { + const inputGitRepository: GitRepository = { + name: 'super-feature-repo', + }; + + const inputIdentityRef: IdentityRef = { + displayName: 'Jane Doe', + uniqueName: 'DOMAINjdoe', + }; + + const inputPullRequest: GitPullRequest = { + pullRequestId: 7181, + repository: inputGitRepository, + title: 'My Awesome New Feature', + createdBy: inputIdentityRef, + creationDate: new Date('2020-09-12T06:10:23.9325232Z'), + sourceRefName: 'refs/heads/topic/super-awesome-feature', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + }; + + const inputBaseUrl = + 'https://host.com/myOrg/_git/super-feature-repo/pullrequest'; + + const outputPullRequest: PullRequest = { + pullRequestId: 7181, + repoName: 'super-feature-repo', + title: 'My Awesome New Feature', + uniqueName: 'DOMAINjdoe', + createdBy: 'Jane Doe', + creationDate: new Date('2020-09-12T06:10:23.9325232Z'), + sourceRefName: 'refs/heads/topic/super-awesome-feature', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + link: 'https://host.com/myOrg/_git/super-feature-repo/pullrequest/7181', + }; + + expect(mappedPullRequest(inputPullRequest, inputBaseUrl)).toEqual( + outputPullRequest, + ); }); }); }); diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts index 3da4dff11d..89e32aa2f3 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts @@ -16,12 +16,16 @@ import { Logger } from 'winston'; import { WebApi } from 'azure-devops-node-api'; -import { RepoBuild } from './types'; import { Build, BuildResult, BuildStatus, } from 'azure-devops-node-api/interfaces/BuildInterfaces'; +import { + GitPullRequest, + GitPullRequestSearchCriteria, +} from 'azure-devops-node-api/interfaces/GitInterfaces'; +import { PullRequest, PullRequestOptions, RepoBuild } from './types'; export class AzureDevOpsApi { constructor( @@ -88,14 +92,48 @@ export class AzureDevOpsApi { ); const repoBuilds: RepoBuild[] = buildList.map(build => { - return repoBuildFromBuild(build); + return mappedRepoBuild(build); }); return repoBuilds; } + + async getPullRequests( + projectName: string, + repoName: string, + options: PullRequestOptions, + ) { + if (this.logger) { + this.logger.debug( + `Calling Azure DevOps REST API, getting up to ${top} Pull Requests for Repository ${repoName} for Project ${projectName}`, + ); + } + + const gitRepository = await this.getGitRepository(projectName, repoName); + const client = await this.webApi.getGitApi(); + const searchCriteria: GitPullRequestSearchCriteria = { + status: options.status, + }; + const gitPullRequests = await client.getPullRequests( + gitRepository.id as string, + searchCriteria, + projectName, + undefined, + undefined, + options.top, + ); + const linkBaseUrl = `${this.webApi.serverUrl}/${encodeURIComponent( + projectName, + )}/_git/${encodeURIComponent(repoName)}/pullrequest`; + const pullRequests: PullRequest[] = gitPullRequests.map(gitPullRequest => { + return mappedPullRequest(gitPullRequest, linkBaseUrl); + }); + + return pullRequests; + } } -export function repoBuildFromBuild(build: Build) { +export function mappedRepoBuild(build: Build) { return { id: build.id, title: [build.definition?.name, build.buildNumber] @@ -108,3 +146,22 @@ export function repoBuildFromBuild(build: Build) { source: `${build.sourceBranch} (${build.sourceVersion?.substr(0, 8)})`, }; } + +export function mappedPullRequest( + pullRequest: GitPullRequest, + linkBaseUrl: string, +) { + return { + pullRequestId: pullRequest.pullRequestId, + repoName: pullRequest.repository?.name, + title: pullRequest.title, + uniqueName: pullRequest.createdBy?.uniqueName, + createdBy: pullRequest.createdBy?.displayName, + creationDate: pullRequest.creationDate, + sourceRefName: pullRequest.sourceRefName, + targetRefName: pullRequest.targetRefName, + status: pullRequest.status, + isDraft: pullRequest.isDraft, + link: `${linkBaseUrl}/${pullRequest.pullRequestId}`, + }; +} diff --git a/plugins/azure-devops-backend/src/api/index.ts b/plugins/azure-devops-backend/src/api/index.ts index 8faf37fe4e..893f0f4f2c 100644 --- a/plugins/azure-devops-backend/src/api/index.ts +++ b/plugins/azure-devops-backend/src/api/index.ts @@ -15,4 +15,4 @@ */ export { AzureDevOpsApi } from './AzureDevOpsApi'; -export type { RepoBuild } from './types'; +export type { RepoBuild, PullRequest } from './types'; diff --git a/plugins/azure-devops-backend/src/api/types.ts b/plugins/azure-devops-backend/src/api/types.ts index b3562f76c6..a53f501f26 100644 --- a/plugins/azure-devops-backend/src/api/types.ts +++ b/plugins/azure-devops-backend/src/api/types.ts @@ -18,6 +18,7 @@ import { BuildResult, BuildStatus, } from 'azure-devops-node-api/interfaces/BuildInterfaces'; +import { PullRequestStatus } from 'azure-devops-node-api/interfaces/GitInterfaces'; export type RepoBuild = { id?: number; @@ -28,3 +29,22 @@ export type RepoBuild = { queueTime?: Date; source: string; }; + +export type PullRequest = { + pullRequestId?: number; + repoName?: string; + title?: string; + uniqueName?: string; + createdBy?: string; + creationDate?: Date; + sourceRefName?: string; + targetRefName?: string; + status?: PullRequestStatus; + isDraft?: boolean; + link: string; +}; + +export type PullRequestOptions = { + top: number; + status: PullRequestStatus; +}; diff --git a/plugins/azure-devops-backend/src/index.ts b/plugins/azure-devops-backend/src/index.ts index a7004ec73e..ff4d3973bf 100644 --- a/plugins/azure-devops-backend/src/index.ts +++ b/plugins/azure-devops-backend/src/index.ts @@ -14,5 +14,5 @@ * limitations under the License. */ export { AzureDevOpsApi } from './api'; -export type { RepoBuild } from './api'; +export type { RepoBuild, PullRequest } from './api'; export * from './service/router'; diff --git a/plugins/azure-devops-backend/src/service/router.test.ts b/plugins/azure-devops-backend/src/service/router.test.ts index a66b86f418..4143291409 100644 --- a/plugins/azure-devops-backend/src/service/router.test.ts +++ b/plugins/azure-devops-backend/src/service/router.test.ts @@ -20,8 +20,11 @@ import express from 'express'; import request from 'supertest'; import { AzureDevOpsApi } from '../api'; import { createRouter } from './router'; -import { RepoBuild } from '../api/types'; -import { GitRepository } from 'azure-devops-node-api/interfaces/GitInterfaces'; +import { PullRequest, RepoBuild } from '../api/types'; +import { + GitRepository, + PullRequestStatus, +} from 'azure-devops-node-api/interfaces/GitInterfaces'; import { Build, BuildResult, @@ -37,6 +40,7 @@ describe('createRouter', () => { getGitRepository: jest.fn(), getBuildList: jest.fn(), getRepoBuilds: jest.fn(), + getPullRequests: jest.fn(), } as any; const router = await createRouter({ azureDevOpsApi, @@ -193,4 +197,67 @@ describe('createRouter', () => { expect(response.body).toEqual(repoBuilds); }); }); + + describe('GET /pull-requests/:projectName/:repoName', () => { + it('fetches a list of pull requests', async () => { + const firstPullRequest: PullRequest = { + pullRequestId: 7181, + repoName: 'super-feature-repo', + title: 'My Awesome New Feature', + createdBy: 'Jane Doe', + creationDate: undefined, + sourceRefName: 'refs/heads/topic/super-awesome-feature', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + link: 'https://host.com/myOrg/_git/super-feature-repo/pullrequest/7181', + }; + + const secondPullRequest: PullRequest = { + pullRequestId: 7182, + repoName: 'super-feature-repo', + title: 'Refactoring My Awesome New Feature', + createdBy: 'Jane Doe', + creationDate: undefined, + sourceRefName: 'refs/heads/topic/refactor-super-awesome-feature', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + link: 'https://host.com/myOrg/_git/super-feature-repo/pullrequest/7182', + }; + + const thirdPullRequest: PullRequest = { + pullRequestId: 7183, + repoName: 'super-feature-repo', + title: 'Bug Fix for My Awesome New Feature', + createdBy: 'Jane Doe', + creationDate: undefined, + sourceRefName: 'refs/heads/topic/fix-super-awesome-feature', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + link: 'https://host.com/myOrg/_git/super-feature-repo/pullrequest/7183', + }; + + const pullRequests: PullRequest[] = [ + firstPullRequest, + secondPullRequest, + thirdPullRequest, + ]; + + azureDevOpsApi.getPullRequests.mockResolvedValueOnce(pullRequests); + + const response = await request(app) + .get('/pull-requests/myProject/myRepo') + .query({ top: '50', status: 1 }); + + expect(azureDevOpsApi.getPullRequests).toHaveBeenCalledWith( + 'myProject', + 'myRepo', + { status: 1, top: 50 }, + ); + expect(response.status).toEqual(200); + expect(response.body).toEqual(pullRequests); + }); + }); }); diff --git a/plugins/azure-devops-backend/src/service/router.ts b/plugins/azure-devops-backend/src/service/router.ts index 1fc86128e6..0929d5cde9 100644 --- a/plugins/azure-devops-backend/src/service/router.ts +++ b/plugins/azure-devops-backend/src/service/router.ts @@ -21,6 +21,8 @@ import { Logger } from 'winston'; import { Config } from '@backstage/config'; import { getPersonalAccessTokenHandler, WebApi } from 'azure-devops-node-api'; import { AzureDevOpsApi } from '../api'; +import { PullRequestStatus } from 'azure-devops-node-api/interfaces/GitInterfaces'; +import { PullRequestOptions } from '../api/types'; const DEFAULT_TOP: number = 10; @@ -84,6 +86,24 @@ export async function createRouter( res.status(200).json(gitRepository); }); + router.get('/pull-requests/:projectName/:repoName', async (req, res) => { + const { projectName, repoName } = req.params; + const top = req.query.top ? Number(req.query.top) : DEFAULT_TOP; + const status = req.query.status + ? Number(req.query.status) + : PullRequestStatus.Active; + const pullRequestOptions: PullRequestOptions = { + top: top, + status: status, + }; + const gitPullRequest = await azureDevOpsApi.getPullRequests( + projectName, + repoName, + pullRequestOptions, + ); + res.status(200).json(gitPullRequest); + }); + router.use(errorHandler()); return router; } diff --git a/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.test.ts b/plugins/catalog-backend/src/database/DefaultProcessingDatabase.test.ts similarity index 99% rename from plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.test.ts rename to plugins/catalog-backend/src/database/DefaultProcessingDatabase.test.ts index 5f509747f3..00b3de2fc7 100644 --- a/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.test.ts +++ b/plugins/catalog-backend/src/database/DefaultProcessingDatabase.test.ts @@ -28,7 +28,7 @@ import { DbRefreshStateRow, DbRelationsRow, } from './tables'; -import { createRandomRefreshInterval } from '../refresh'; +import { createRandomRefreshInterval } from '../processing/refresh'; import { timestampToDateTime } from './conversion'; import { generateStableHash } from './util'; diff --git a/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.ts b/plugins/catalog-backend/src/database/DefaultProcessingDatabase.ts similarity index 99% rename from plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.ts rename to plugins/catalog-backend/src/database/DefaultProcessingDatabase.ts index b47154a46f..9785ac3935 100644 --- a/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.ts +++ b/plugins/catalog-backend/src/database/DefaultProcessingDatabase.ts @@ -33,7 +33,7 @@ import { UpdateEntityCacheOptions, } from './types'; import { DeferredEntity } from '../processing/types'; -import { RefreshIntervalFunction } from '../refresh'; +import { RefreshIntervalFunction } from '../processing/refresh'; import { rethrowError, timestampToDateTime } from './conversion'; import { initDatabaseMetrics } from './metrics'; import { diff --git a/plugins/catalog-backend/src/next/database/conversion.test.ts b/plugins/catalog-backend/src/database/conversion.test.ts similarity index 100% rename from plugins/catalog-backend/src/next/database/conversion.test.ts rename to plugins/catalog-backend/src/database/conversion.test.ts diff --git a/plugins/catalog-backend/src/next/database/conversion.ts b/plugins/catalog-backend/src/database/conversion.ts similarity index 100% rename from plugins/catalog-backend/src/next/database/conversion.ts rename to plugins/catalog-backend/src/database/conversion.ts diff --git a/plugins/catalog-backend/src/next/database/metrics.ts b/plugins/catalog-backend/src/database/metrics.ts similarity index 97% rename from plugins/catalog-backend/src/next/database/metrics.ts rename to plugins/catalog-backend/src/database/metrics.ts index 4ad319ed10..b6c4e248dc 100644 --- a/plugins/catalog-backend/src/next/database/metrics.ts +++ b/plugins/catalog-backend/src/database/metrics.ts @@ -15,7 +15,7 @@ */ import { Knex } from 'knex'; -import { createGaugeMetric } from '../metrics'; +import { createGaugeMetric } from '../util/metrics'; import { DbRefreshStateRow, DbRelationsRow, DbLocationsRow } from './tables'; export function initDatabaseMetrics(knex: Knex) { diff --git a/plugins/catalog-backend/src/next/database/migrations.ts b/plugins/catalog-backend/src/database/migrations.ts similarity index 100% rename from plugins/catalog-backend/src/next/database/migrations.ts rename to plugins/catalog-backend/src/database/migrations.ts diff --git a/plugins/catalog-backend/src/next/database/tables.ts b/plugins/catalog-backend/src/database/tables.ts similarity index 100% rename from plugins/catalog-backend/src/next/database/tables.ts rename to plugins/catalog-backend/src/database/tables.ts diff --git a/plugins/catalog-backend/src/next/database/types.ts b/plugins/catalog-backend/src/database/types.ts similarity index 100% rename from plugins/catalog-backend/src/next/database/types.ts rename to plugins/catalog-backend/src/database/types.ts diff --git a/plugins/catalog-backend/src/next/database/util.ts b/plugins/catalog-backend/src/database/util.ts similarity index 100% rename from plugins/catalog-backend/src/next/database/util.ts rename to plugins/catalog-backend/src/database/util.ts diff --git a/plugins/catalog-backend/src/index.ts b/plugins/catalog-backend/src/index.ts index 4d84330f27..7ad888bee4 100644 --- a/plugins/catalog-backend/src/index.ts +++ b/plugins/catalog-backend/src/index.ts @@ -25,4 +25,6 @@ export * from './ingestion'; export * from './legacy'; export * from './search'; export * from './util'; -export * from './next'; +export * from './processing'; +export * from './providers'; +export * from './service'; diff --git a/plugins/catalog-backend/src/ingestion/processors/AwsS3DiscoveryProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/AwsS3DiscoveryProcessor.test.ts index 435ad368c7..064a2ad724 100644 --- a/plugins/catalog-backend/src/ingestion/processors/AwsS3DiscoveryProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/AwsS3DiscoveryProcessor.test.ts @@ -37,13 +37,8 @@ AWSMock.mock( Buffer.from( require('fs').readFileSync( path.resolve( - 'src', - 'ingestion', - 'processors', - '__fixtures__', - 'fileReaderProcessor', - 'awsS3', - 'awsS3-mock-object.txt', + __dirname, + '__fixtures__/fileReaderProcessor/awsS3/awsS3-mock-object.txt', ), ), ), diff --git a/plugins/catalog-backend/src/ingestion/processors/FileReaderProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/FileReaderProcessor.test.ts index 41c7e4f732..8a5f202e5e 100644 --- a/plugins/catalog-backend/src/ingestion/processors/FileReaderProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/FileReaderProcessor.test.ts @@ -23,13 +23,7 @@ import { import path from 'path'; describe('FileReaderProcessor', () => { - const fixturesRoot = path.join( - 'src', - 'ingestion', - 'processors', - '__fixtures__', - 'fileReaderProcessor', - ); + const fixturesRoot = path.join(__dirname, '__fixtures__/fileReaderProcessor'); it('should load from file', async () => { const processor = new FileReaderProcessor(); diff --git a/plugins/catalog-backend/src/legacy/service/CatalogBuilder.test.ts b/plugins/catalog-backend/src/legacy/service/CatalogBuilder.test.ts index 1b366130ed..926aa67635 100644 --- a/plugins/catalog-backend/src/legacy/service/CatalogBuilder.test.ts +++ b/plugins/catalog-backend/src/legacy/service/CatalogBuilder.test.ts @@ -23,7 +23,7 @@ import { DatabaseManager } from '../database'; import { CatalogProcessorParser } from '../../ingestion'; import * as result from '../../ingestion/processors/results'; import { CatalogBuilder } from './CatalogBuilder'; -import { CatalogEnvironment } from '../../next'; +import { CatalogEnvironment } from '../../service'; const dummyEntity = { apiVersion: 'backstage.io/v1alpha1', diff --git a/plugins/catalog-backend/src/legacy/service/CatalogBuilder.ts b/plugins/catalog-backend/src/legacy/service/CatalogBuilder.ts index 1a151b77d6..b8e45c7cf3 100644 --- a/plugins/catalog-backend/src/legacy/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/legacy/service/CatalogBuilder.ts @@ -64,7 +64,7 @@ import { } from '../../ingestion/processors/PlaceholderProcessor'; import { defaultEntityDataParser } from '../../ingestion/processors/util/parse'; import { LocationAnalyzer } from '../../ingestion/types'; -import { CatalogEnvironment, NextCatalogBuilder } from '../../next'; +import { CatalogEnvironment, NextCatalogBuilder } from '../../service'; /** * A builder that helps wire up all of the component parts of the catalog. diff --git a/plugins/catalog-backend/src/legacy/service/router.test.ts b/plugins/catalog-backend/src/legacy/service/router.test.ts index d15d3799b5..edc7d852ac 100644 --- a/plugins/catalog-backend/src/legacy/service/router.test.ts +++ b/plugins/catalog-backend/src/legacy/service/router.test.ts @@ -25,7 +25,7 @@ import { LocationResponse, LocationsCatalog } from '../catalog/types'; import { HigherOrderOperation } from '../ingestion/types'; import { createRouter } from './router'; import { basicEntityFilter } from '../../service/request'; -import { RefreshService } from '../../next'; +import { RefreshService } from '../../service'; describe('createRouter readonly disabled', () => { let entitiesCatalog: jest.Mocked>; diff --git a/plugins/catalog-backend/src/legacy/service/router.ts b/plugins/catalog-backend/src/legacy/service/router.ts index 4dfa9e49f9..e7d097a9c7 100644 --- a/plugins/catalog-backend/src/legacy/service/router.ts +++ b/plugins/catalog-backend/src/legacy/service/router.ts @@ -34,7 +34,7 @@ import { RefreshService, LocationService, RefreshOptions, -} from '../../next/types'; +} from '../../service/types'; import { basicEntityFilter, parseEntityFilterParams, diff --git a/plugins/catalog-backend/src/next/Context/ContextWithValue.ts b/plugins/catalog-backend/src/next/Context/ContextWithValue.ts deleted file mode 100644 index cc88c03679..0000000000 --- a/plugins/catalog-backend/src/next/Context/ContextWithValue.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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. - */ - -import { Context, ContextKey } from './types'; - -/** - * A Context implementation that holds a single value, optionally extending an existing context. - */ -export class ContextWithValue implements Context { - static create(parent: Context, key: ContextKey, value: unknown) { - return new ContextWithValue(parent, key, value); - } - - private constructor( - private readonly parent: Context, - private readonly key: ContextKey, - private readonly value: unknown, - ) {} - - getContextValue(key: ContextKey): T { - if (this.key === key) { - return this.value as T; - } - return this.parent.getContextValue(key); - } -} diff --git a/plugins/catalog-backend/src/next/Context/TransactionValue.test.ts b/plugins/catalog-backend/src/next/Context/TransactionValue.test.ts deleted file mode 100644 index 20165eff2f..0000000000 --- a/plugins/catalog-backend/src/next/Context/TransactionValue.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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. - */ -import { TransactionValue } from './TransactionValue'; -import { Knex } from 'knex'; -import { BackgroundContext } from './BackgroundContext'; - -describe('TransactionValue Context', () => { - it('should be able to store tx values and retrieve them from a context', () => { - const tx = {} as Knex.Transaction; - const ctx = new BackgroundContext(); - - const nextCtx = TransactionValue.in(ctx, tx); - - expect(TransactionValue.from(nextCtx)).toBe(tx); - }); - - it('should throw when there is no tx value in the context', () => { - const ctx = new BackgroundContext(); - - expect(() => TransactionValue.from(ctx)).toThrow( - /No transaction available in context/, - ); - }); -}); diff --git a/plugins/catalog-backend/src/next/Context/TransactionValue.ts b/plugins/catalog-backend/src/next/Context/TransactionValue.ts deleted file mode 100644 index af069c6d8e..0000000000 --- a/plugins/catalog-backend/src/next/Context/TransactionValue.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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. - */ - -import { Context, ContextKey } from './types'; -import { Knex } from 'knex'; -import { ContextWithValue } from './ContextWithValue'; - -const transactionContextKey = new ContextKey( - undefined, -); - -/** - * TransactionValue handles the wrapping of a knex transaction in a Context. - */ -export class TransactionValue { - static in(parent: Context, tx: Knex.Transaction) { - return ContextWithValue.create(parent, transactionContextKey, tx); - } - - static from(context: Context): Knex.Transaction { - const transaction = context.getContextValue(transactionContextKey); - if (!transaction) { - throw new Error(`No transaction available in context`); - } - return transaction; - } -} diff --git a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.test.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.test.ts similarity index 97% rename from plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.test.ts rename to plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.test.ts index 430d7f251f..044c3e7c4b 100644 --- a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.test.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.test.ts @@ -18,10 +18,10 @@ import { getVoidLogger } from '@backstage/backend-common'; import { Hash } from 'crypto'; import { DateTime } from 'luxon'; import waitForExpect from 'wait-for-expect'; -import { DefaultProcessingDatabase } from './database/DefaultProcessingDatabase'; +import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase'; import { DefaultCatalogProcessingEngine } from './DefaultCatalogProcessingEngine'; -import { CatalogProcessingOrchestrator } from './processing/types'; -import { Stitcher } from './stitching/Stitcher'; +import { CatalogProcessingOrchestrator } from './types'; +import { Stitcher } from '../stitching/Stitcher'; describe('DefaultCatalogProcessingEngine', () => { const db = { diff --git a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts similarity index 97% rename from plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts rename to plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts index 346a912009..d0faff25d5 100644 --- a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts @@ -23,20 +23,20 @@ import { serializeError } from '@backstage/errors'; import { Hash } from 'crypto'; import stableStringify from 'fast-json-stable-stringify'; import { Logger } from 'winston'; -import { ProcessingDatabase, RefreshStateItem } from './database/types'; -import { createCounterMetric, createSummaryMetric } from './metrics'; -import { - CatalogProcessingOrchestrator, - EntityProcessingResult, -} from './processing/types'; -import { Stitcher } from './stitching/Stitcher'; -import { startTaskPipeline } from './TaskPipeline'; +import { ProcessingDatabase, RefreshStateItem } from '../database/types'; +import { createCounterMetric, createSummaryMetric } from '../util/metrics'; import { CatalogProcessingEngine, + CatalogProcessingOrchestrator, + EntityProcessingResult, +} from '../processing/types'; +import { Stitcher } from '../stitching/Stitcher'; +import { startTaskPipeline } from './TaskPipeline'; +import { EntityProvider, EntityProviderConnection, EntityProviderMutation, -} from './types'; +} from '../providers/types'; const CACHE_TTL = 5; diff --git a/plugins/catalog-backend/src/next/processing/DefaultCatalogProcessingOrchestrator.test.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.test.ts similarity index 97% rename from plugins/catalog-backend/src/next/processing/DefaultCatalogProcessingOrchestrator.test.ts rename to plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.test.ts index 098f3ac14d..5ae4d58ec7 100644 --- a/plugins/catalog-backend/src/next/processing/DefaultCatalogProcessingOrchestrator.test.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.test.ts @@ -30,10 +30,10 @@ import { CatalogProcessorEmit, CatalogProcessorParser, results, -} from '../../ingestion'; -import { CatalogRulesEnforcer } from '../../ingestion/CatalogRules'; +} from '../ingestion'; +import { CatalogRulesEnforcer } from '../ingestion/CatalogRules'; import { DefaultCatalogProcessingOrchestrator } from './DefaultCatalogProcessingOrchestrator'; -import { defaultEntityDataParser } from '../../ingestion/processors/util/parse'; +import { defaultEntityDataParser } from '../ingestion/processors/util/parse'; import { ConfigReader } from '@backstage/config'; class FooBarProcessor implements CatalogProcessor { diff --git a/plugins/catalog-backend/src/next/processing/DefaultCatalogProcessingOrchestrator.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts similarity index 98% rename from plugins/catalog-backend/src/next/processing/DefaultCatalogProcessingOrchestrator.ts rename to plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts index 7a3977bcca..e973dcbf0c 100644 --- a/plugins/catalog-backend/src/next/processing/DefaultCatalogProcessingOrchestrator.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts @@ -31,8 +31,8 @@ import { Logger } from 'winston'; import { CatalogProcessor, CatalogProcessorParser, -} from '../../ingestion/processors'; -import * as results from '../../ingestion/processors/results'; +} from '../ingestion/processors'; +import * as results from '../ingestion/processors/results'; import { CatalogProcessingOrchestrator, EntityProcessingRequest, @@ -48,7 +48,7 @@ import { validateEntityEnvelope, isObject, } from './util'; -import { CatalogRulesEnforcer } from '../../ingestion/CatalogRules'; +import { CatalogRulesEnforcer } from '../ingestion/CatalogRules'; import { ProcessorCacheManager } from './ProcessorCacheManager'; type Context = { diff --git a/plugins/catalog-backend/src/next/processing/ProcessorCacheManager.test.ts b/plugins/catalog-backend/src/processing/ProcessorCacheManager.test.ts similarity index 98% rename from plugins/catalog-backend/src/next/processing/ProcessorCacheManager.test.ts rename to plugins/catalog-backend/src/processing/ProcessorCacheManager.test.ts index 44c96da1ca..e7f9fb313b 100644 --- a/plugins/catalog-backend/src/next/processing/ProcessorCacheManager.test.ts +++ b/plugins/catalog-backend/src/processing/ProcessorCacheManager.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { CatalogProcessor } from '../../ingestion/processors'; +import { CatalogProcessor } from '../ingestion/processors'; import { ProcessorCacheManager } from './ProcessorCacheManager'; class MyProcessor implements CatalogProcessor { diff --git a/plugins/catalog-backend/src/next/processing/ProcessorCacheManager.ts b/plugins/catalog-backend/src/processing/ProcessorCacheManager.ts similarity index 96% rename from plugins/catalog-backend/src/next/processing/ProcessorCacheManager.ts rename to plugins/catalog-backend/src/processing/ProcessorCacheManager.ts index 16945b7cc9..378974e33e 100644 --- a/plugins/catalog-backend/src/next/processing/ProcessorCacheManager.ts +++ b/plugins/catalog-backend/src/processing/ProcessorCacheManager.ts @@ -15,8 +15,8 @@ */ import { JsonObject, JsonValue } from '@backstage/config'; -import { CatalogProcessor } from '../../ingestion/processors'; -import { CatalogProcessorCache } from '../../ingestion/processors/types'; +import { CatalogProcessor } from '../ingestion/processors'; +import { CatalogProcessorCache } from '../ingestion/processors/types'; import { isObject } from './util'; class SingleProcessorSubCache implements CatalogProcessorCache { diff --git a/plugins/catalog-backend/src/next/processing/ProcessorOutputCollector.ts b/plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts similarity index 96% rename from plugins/catalog-backend/src/next/processing/ProcessorOutputCollector.ts rename to plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts index 507def964a..dfaa25418b 100644 --- a/plugins/catalog-backend/src/next/processing/ProcessorOutputCollector.ts +++ b/plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts @@ -22,8 +22,8 @@ import { stringifyLocationReference, } from '@backstage/catalog-model'; import { Logger } from 'winston'; -import { CatalogProcessorResult } from '../../ingestion'; -import { locationSpecToLocationEntity } from '../util'; +import { CatalogProcessorResult } from '../ingestion'; +import { locationSpecToLocationEntity } from '../util/conversion'; import { DeferredEntity } from './types'; import { getEntityLocationRef, diff --git a/plugins/catalog-backend/src/next/TaskPipeline.test.ts b/plugins/catalog-backend/src/processing/TaskPipeline.test.ts similarity index 100% rename from plugins/catalog-backend/src/next/TaskPipeline.test.ts rename to plugins/catalog-backend/src/processing/TaskPipeline.test.ts diff --git a/plugins/catalog-backend/src/next/TaskPipeline.ts b/plugins/catalog-backend/src/processing/TaskPipeline.ts similarity index 100% rename from plugins/catalog-backend/src/next/TaskPipeline.ts rename to plugins/catalog-backend/src/processing/TaskPipeline.ts diff --git a/plugins/catalog-backend/src/next/processing/index.ts b/plugins/catalog-backend/src/processing/index.ts similarity index 85% rename from plugins/catalog-backend/src/next/processing/index.ts rename to plugins/catalog-backend/src/processing/index.ts index 9e03f18f28..dc8b07eceb 100644 --- a/plugins/catalog-backend/src/next/processing/index.ts +++ b/plugins/catalog-backend/src/processing/index.ts @@ -16,8 +16,12 @@ export type { CatalogProcessingOrchestrator, + CatalogProcessingEngine, EntityProcessingRequest, EntityProcessingResult, DeferredEntity, } from './types'; export { DefaultCatalogProcessingOrchestrator } from './DefaultCatalogProcessingOrchestrator'; + +export { createRandomRefreshInterval } from './refresh'; +export type { RefreshIntervalFunction } from './refresh'; diff --git a/plugins/catalog-backend/src/next/refresh.ts b/plugins/catalog-backend/src/processing/refresh.ts similarity index 100% rename from plugins/catalog-backend/src/next/refresh.ts rename to plugins/catalog-backend/src/processing/refresh.ts diff --git a/plugins/catalog-backend/src/next/processing/types.ts b/plugins/catalog-backend/src/processing/types.ts similarity index 93% rename from plugins/catalog-backend/src/next/processing/types.ts rename to plugins/catalog-backend/src/processing/types.ts index 8b533714ce..4b16ccef32 100644 --- a/plugins/catalog-backend/src/next/processing/types.ts +++ b/plugins/catalog-backend/src/processing/types.ts @@ -44,3 +44,8 @@ export type DeferredEntity = { entity: Entity; locationKey?: string; }; + +export interface CatalogProcessingEngine { + start(): Promise; + stop(): Promise; +} diff --git a/plugins/catalog-backend/src/next/processing/util.ts b/plugins/catalog-backend/src/processing/util.ts similarity index 100% rename from plugins/catalog-backend/src/next/processing/util.ts rename to plugins/catalog-backend/src/processing/util.ts diff --git a/plugins/catalog-backend/src/next/ConfigLocationEntityProvider.test.ts b/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.test.ts similarity index 94% rename from plugins/catalog-backend/src/next/ConfigLocationEntityProvider.test.ts rename to plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.test.ts index ef3e47eb8b..0663ad8d04 100644 --- a/plugins/catalog-backend/src/next/ConfigLocationEntityProvider.test.ts +++ b/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.test.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { resolvePackagePath } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import path from 'path'; import { ConfigLocationEntityProvider } from './ConfigLocationEntityProvider'; @@ -44,10 +43,7 @@ describe('ConfigLocationEntityProvider', () => { { entity: expect.objectContaining({ spec: { - target: path.join( - resolvePackagePath('@backstage/plugin-catalog-backend'), - './lols.yaml', - ), + target: path.join(process.cwd(), 'lols.yaml'), type: 'file', }, }), diff --git a/plugins/catalog-backend/src/next/ConfigLocationEntityProvider.ts b/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.ts similarity index 94% rename from plugins/catalog-backend/src/next/ConfigLocationEntityProvider.ts rename to plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.ts index 5deca09944..389cd8d9ff 100644 --- a/plugins/catalog-backend/src/next/ConfigLocationEntityProvider.ts +++ b/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.ts @@ -16,9 +16,9 @@ import { Config } from '@backstage/config'; import path from 'path'; -import { getEntityLocationRef } from './processing/util'; +import { getEntityLocationRef } from '../processing/util'; import { EntityProvider, EntityProviderConnection } from './types'; -import { locationSpecToLocationEntity } from './util'; +import { locationSpecToLocationEntity } from '../util/conversion'; export class ConfigLocationEntityProvider implements EntityProvider { constructor(private readonly config: Config) {} diff --git a/plugins/catalog-backend/src/next/DefaultLocationStore.test.ts b/plugins/catalog-backend/src/providers/DefaultLocationStore.test.ts similarity index 98% rename from plugins/catalog-backend/src/next/DefaultLocationStore.test.ts rename to plugins/catalog-backend/src/providers/DefaultLocationStore.test.ts index f3cefeb14f..b9d129de5f 100644 --- a/plugins/catalog-backend/src/next/DefaultLocationStore.test.ts +++ b/plugins/catalog-backend/src/providers/DefaultLocationStore.test.ts @@ -15,7 +15,7 @@ */ import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; import { v4 as uuid } from 'uuid'; -import { applyDatabaseMigrations } from './database/migrations'; +import { applyDatabaseMigrations } from '../database/migrations'; import { DefaultLocationStore } from './DefaultLocationStore'; describe('DefaultLocationStore', () => { diff --git a/plugins/catalog-backend/src/next/DefaultLocationStore.ts b/plugins/catalog-backend/src/providers/DefaultLocationStore.ts similarity index 93% rename from plugins/catalog-backend/src/next/DefaultLocationStore.ts rename to plugins/catalog-backend/src/providers/DefaultLocationStore.ts index c9ffe9a682..9214cd0bc1 100644 --- a/plugins/catalog-backend/src/next/DefaultLocationStore.ts +++ b/plugins/catalog-backend/src/providers/DefaultLocationStore.ts @@ -18,14 +18,11 @@ import { Location, LocationSpec } from '@backstage/catalog-model'; import { ConflictError, NotFoundError } from '@backstage/errors'; import { Knex } from 'knex'; import { v4 as uuid } from 'uuid'; -import { DbLocationsRow } from './database/tables'; -import { getEntityLocationRef } from './processing/util'; -import { - EntityProvider, - EntityProviderConnection, - LocationStore, -} from './types'; -import { locationSpecToLocationEntity } from './util'; +import { DbLocationsRow } from '../database/tables'; +import { getEntityLocationRef } from '../processing/util'; +import { EntityProvider, EntityProviderConnection } from './types'; +import { locationSpecToLocationEntity } from '../util/conversion'; +import { LocationStore } from '../service'; export class DefaultLocationStore implements LocationStore, EntityProvider { private _connection: EntityProviderConnection | undefined; diff --git a/plugins/catalog-backend/src/next/Context/types.ts b/plugins/catalog-backend/src/providers/index.ts similarity index 80% rename from plugins/catalog-backend/src/next/Context/types.ts rename to plugins/catalog-backend/src/providers/index.ts index 062bc884e9..3b4a1c4b7e 100644 --- a/plugins/catalog-backend/src/next/Context/types.ts +++ b/plugins/catalog-backend/src/providers/index.ts @@ -14,10 +14,8 @@ * limitations under the License. */ -export class ContextKey { - constructor(readonly defaultValue: T) {} -} - -export interface Context { - getContextValue(key: ContextKey): T; -} +export type { + EntityProvider, + EntityProviderConnection, + EntityProviderMutation, +} from './types'; diff --git a/plugins/catalog-backend/src/next/Context/BackgroundContext.ts b/plugins/catalog-backend/src/providers/types.ts similarity index 57% rename from plugins/catalog-backend/src/next/Context/BackgroundContext.ts rename to plugins/catalog-backend/src/providers/types.ts index c41fa8e3c5..90c5f13215 100644 --- a/plugins/catalog-backend/src/next/Context/BackgroundContext.ts +++ b/plugins/catalog-backend/src/providers/types.ts @@ -14,13 +14,17 @@ * limitations under the License. */ -import { Context, ContextKey } from './types'; +import { DeferredEntity } from '../processing'; -/** - * A base Context implementation that does not hold any value. - */ -export class BackgroundContext implements Context { - getContextValue(key: ContextKey): T { - return key.defaultValue; - } +export type EntityProviderMutation = + | { type: 'full'; entities: DeferredEntity[] } + | { type: 'delta'; added: DeferredEntity[]; removed: DeferredEntity[] }; + +export interface EntityProviderConnection { + applyMutation(mutation: EntityProviderMutation): Promise; +} + +export interface EntityProvider { + getProviderName(): string; + connect(connection: EntityProviderConnection): Promise; } diff --git a/plugins/catalog-backend/src/next/DefaultLocationService.test.ts b/plugins/catalog-backend/src/service/DefaultLocationService.test.ts similarity index 98% rename from plugins/catalog-backend/src/next/DefaultLocationService.test.ts rename to plugins/catalog-backend/src/service/DefaultLocationService.test.ts index 78dd9c1009..a089989d20 100644 --- a/plugins/catalog-backend/src/next/DefaultLocationService.test.ts +++ b/plugins/catalog-backend/src/service/DefaultLocationService.test.ts @@ -15,7 +15,7 @@ */ import { DefaultLocationService } from './DefaultLocationService'; -import { CatalogProcessingOrchestrator } from './processing/types'; +import { CatalogProcessingOrchestrator } from '../processing/types'; import { LocationStore } from './types'; describe('DefaultLocationServiceTest', () => { diff --git a/plugins/catalog-backend/src/next/DefaultLocationService.ts b/plugins/catalog-backend/src/service/DefaultLocationService.ts similarity index 97% rename from plugins/catalog-backend/src/next/DefaultLocationService.ts rename to plugins/catalog-backend/src/service/DefaultLocationService.ts index 678ce9791c..2166341935 100644 --- a/plugins/catalog-backend/src/next/DefaultLocationService.ts +++ b/plugins/catalog-backend/src/service/DefaultLocationService.ts @@ -23,9 +23,9 @@ import { import { CatalogProcessingOrchestrator, DeferredEntity, -} from './processing/types'; +} from '../processing/types'; import { LocationService, LocationStore } from './types'; -import { locationSpecToMetadataName } from './util'; +import { locationSpecToMetadataName } from '../util/conversion'; export class DefaultLocationService implements LocationService { constructor( diff --git a/plugins/catalog-backend/src/next/DefaultRefreshService.test.ts b/plugins/catalog-backend/src/service/DefaultRefreshService.test.ts similarity index 95% rename from plugins/catalog-backend/src/next/DefaultRefreshService.test.ts rename to plugins/catalog-backend/src/service/DefaultRefreshService.test.ts index 7b6a181375..0ea475a528 100644 --- a/plugins/catalog-backend/src/next/DefaultRefreshService.test.ts +++ b/plugins/catalog-backend/src/service/DefaultRefreshService.test.ts @@ -19,16 +19,16 @@ import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; import { createHash } from 'crypto'; import { Knex } from 'knex'; import { Logger } from 'winston'; -import { applyDatabaseMigrations } from './database/migrations'; -import { DefaultProcessingDatabase } from './database/DefaultProcessingDatabase'; +import { applyDatabaseMigrations } from '../database/migrations'; +import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase'; import { DbRefreshStateReferencesRow, DbRefreshStateRow, -} from './database/tables'; -import { ProcessingDatabase } from './database/types'; -import { DefaultCatalogProcessingEngine } from './DefaultCatalogProcessingEngine'; -import { EntityProcessingRequest } from './processing/types'; -import { Stitcher } from './stitching/Stitcher'; +} from '../database/tables'; +import { ProcessingDatabase } from '../database/types'; +import { DefaultCatalogProcessingEngine } from '../processing/DefaultCatalogProcessingEngine'; +import { EntityProcessingRequest } from '../processing/types'; +import { Stitcher } from '../stitching/Stitcher'; import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import { v4 as uuid } from 'uuid'; import { DefaultRefreshService } from './DefaultRefreshService'; diff --git a/plugins/catalog-backend/src/next/DefaultRefreshService.ts b/plugins/catalog-backend/src/service/DefaultRefreshService.ts similarity index 95% rename from plugins/catalog-backend/src/next/DefaultRefreshService.ts rename to plugins/catalog-backend/src/service/DefaultRefreshService.ts index cfc7fa220b..3b982a0e46 100644 --- a/plugins/catalog-backend/src/next/DefaultRefreshService.ts +++ b/plugins/catalog-backend/src/service/DefaultRefreshService.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { DefaultProcessingDatabase } from './database/DefaultProcessingDatabase'; +import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase'; import { RefreshOptions, RefreshService } from './types'; export class DefaultRefreshService implements RefreshService { diff --git a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts b/plugins/catalog-backend/src/service/NextCatalogBuilder.ts similarity index 95% rename from plugins/catalog-backend/src/next/NextCatalogBuilder.ts rename to plugins/catalog-backend/src/service/NextCatalogBuilder.ts index 8ca93d4bd7..60a1668f37 100644 --- a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/NextCatalogBuilder.ts @@ -59,29 +59,27 @@ import { } from '../ingestion/processors/PlaceholderProcessor'; import { defaultEntityDataParser } from '../ingestion/processors/util/parse'; import { LocationAnalyzer } from '../ingestion/types'; -import { - CatalogProcessingEngine, - EntityProvider, - LocationService, -} from '../next/types'; -import { ConfigLocationEntityProvider } from './ConfigLocationEntityProvider'; -import { DefaultProcessingDatabase } from './database/DefaultProcessingDatabase'; -import { applyDatabaseMigrations } from './database/migrations'; -import { DefaultCatalogProcessingEngine } from './DefaultCatalogProcessingEngine'; +import { EntityProvider } from '../providers/types'; +import { CatalogProcessingEngine } from '../processing/types'; +import { ConfigLocationEntityProvider } from '../providers/ConfigLocationEntityProvider'; +import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase'; +import { applyDatabaseMigrations } from '../database/migrations'; +import { DefaultCatalogProcessingEngine } from '../processing/DefaultCatalogProcessingEngine'; import { DefaultLocationService } from './DefaultLocationService'; -import { DefaultLocationStore } from './DefaultLocationStore'; +import { DefaultLocationStore } from '../providers/DefaultLocationStore'; import { NextEntitiesCatalog } from './NextEntitiesCatalog'; -import { DefaultCatalogProcessingOrchestrator } from './processing/DefaultCatalogProcessingOrchestrator'; -import { Stitcher } from './stitching/Stitcher'; +import { DefaultCatalogProcessingOrchestrator } from '../processing/DefaultCatalogProcessingOrchestrator'; +import { Stitcher } from '../stitching/Stitcher'; import { createRandomRefreshInterval, RefreshIntervalFunction, -} from './refresh'; +} from '../processing/refresh'; import { createNextRouter } from './NextRouter'; import { DefaultRefreshService } from './DefaultRefreshService'; import { DefaultCatalogRulesEnforcer } from '../ingestion/CatalogRules'; import { Config } from '@backstage/config'; import { Logger } from 'winston'; +import { LocationService } from './types'; export type CatalogEnvironment = { logger: Logger; diff --git a/plugins/catalog-backend/src/next/NextEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts similarity index 98% rename from plugins/catalog-backend/src/next/NextEntitiesCatalog.test.ts rename to plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts index 47e92445e9..082aa47df5 100644 --- a/plugins/catalog-backend/src/next/NextEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts @@ -18,12 +18,12 @@ import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import { Knex } from 'knex'; import { v4 as uuid } from 'uuid'; -import { applyDatabaseMigrations } from './database/migrations'; +import { applyDatabaseMigrations } from '../database/migrations'; import { DbFinalEntitiesRow, DbRefreshStateReferencesRow, DbRefreshStateRow, -} from './database/tables'; +} from '../database/tables'; import { NextEntitiesCatalog } from './NextEntitiesCatalog'; describe('NextEntitiesCatalog', () => { diff --git a/plugins/catalog-backend/src/next/NextEntitiesCatalog.ts b/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts similarity index 99% rename from plugins/catalog-backend/src/next/NextEntitiesCatalog.ts rename to plugins/catalog-backend/src/service/NextEntitiesCatalog.ts index 4fde078a8b..0891be045d 100644 --- a/plugins/catalog-backend/src/next/NextEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts @@ -30,7 +30,7 @@ import { DbRefreshStateRow, DbSearchRow, DbPageInfo, -} from './database/tables'; +} from '../database/tables'; function parsePagination(input?: EntityPagination): { limit?: number; diff --git a/plugins/catalog-backend/src/next/NextRouter.test.ts b/plugins/catalog-backend/src/service/NextRouter.test.ts similarity index 99% rename from plugins/catalog-backend/src/next/NextRouter.test.ts rename to plugins/catalog-backend/src/service/NextRouter.test.ts index 489e7efdb5..57911c74fb 100644 --- a/plugins/catalog-backend/src/next/NextRouter.test.ts +++ b/plugins/catalog-backend/src/service/NextRouter.test.ts @@ -22,7 +22,7 @@ import express from 'express'; import request from 'supertest'; import { EntitiesCatalog } from '../catalog'; import { LocationService, RefreshService } from './types'; -import { basicEntityFilter } from '../service/request'; +import { basicEntityFilter } from './request'; import { createNextRouter } from './NextRouter'; describe('createNextRouter readonly disabled', () => { diff --git a/plugins/catalog-backend/src/next/NextRouter.ts b/plugins/catalog-backend/src/service/NextRouter.ts similarity index 100% rename from plugins/catalog-backend/src/next/NextRouter.ts rename to plugins/catalog-backend/src/service/NextRouter.ts diff --git a/plugins/catalog-backend/src/next/index.ts b/plugins/catalog-backend/src/service/index.ts similarity index 77% rename from plugins/catalog-backend/src/next/index.ts rename to plugins/catalog-backend/src/service/index.ts index bbc68651ea..5345f79628 100644 --- a/plugins/catalog-backend/src/next/index.ts +++ b/plugins/catalog-backend/src/service/index.ts @@ -14,21 +14,13 @@ * limitations under the License. */ -export type { CatalogEnvironment } from './NextCatalogBuilder'; -export { NextCatalogBuilder } from './NextCatalogBuilder'; +export type { + LocationService, + RefreshService, + RefreshOptions, + LocationStore, +} from './types'; export { createNextRouter } from './NextRouter'; export type { NextRouterOptions } from './NextRouter'; -export * from './processing'; -export { createRandomRefreshInterval } from './refresh'; -export type { RefreshIntervalFunction } from './refresh'; -export * from './stitching'; -export type { - EntityProvider, - EntityProviderConnection, - EntityProviderMutation, - CatalogProcessingEngine, - LocationService, - LocationStore, - RefreshOptions, - RefreshService, -} from './types'; +export type { CatalogEnvironment } from './NextCatalogBuilder'; +export { NextCatalogBuilder } from './NextCatalogBuilder'; diff --git a/plugins/catalog-backend/src/next/types.ts b/plugins/catalog-backend/src/service/types.ts similarity index 72% rename from plugins/catalog-backend/src/next/types.ts rename to plugins/catalog-backend/src/service/types.ts index 4131b5835b..05d3d00f4d 100644 --- a/plugins/catalog-backend/src/next/types.ts +++ b/plugins/catalog-backend/src/service/types.ts @@ -14,8 +14,7 @@ * limitations under the License. */ -import { Entity, Location, LocationSpec } from '@backstage/catalog-model'; -import { DeferredEntity } from './processing/types'; +import { Entity, LocationSpec, Location } from '@backstage/catalog-model'; export interface LocationService { createLocation( @@ -27,18 +26,6 @@ export interface LocationService { deleteLocation(id: string): Promise; } -export interface LocationStore { - createLocation(spec: LocationSpec): Promise; - listLocations(): Promise; - getLocation(id: string): Promise; - deleteLocation(id: string): Promise; -} - -export interface CatalogProcessingEngine { - start(): Promise; - stop(): Promise; -} - /** * Options for requesting a refresh of entities in the catalog. * @@ -61,15 +48,9 @@ export interface RefreshService { refresh(options: RefreshOptions): Promise; } -export type EntityProviderMutation = - | { type: 'full'; entities: DeferredEntity[] } - | { type: 'delta'; added: DeferredEntity[]; removed: DeferredEntity[] }; - -export interface EntityProviderConnection { - applyMutation(mutation: EntityProviderMutation): Promise; -} - -export interface EntityProvider { - getProviderName(): string; - connect(connection: EntityProviderConnection): Promise; +export interface LocationStore { + createLocation(spec: LocationSpec): Promise; + listLocations(): Promise; + getLocation(id: string): Promise; + deleteLocation(id: string): Promise; } diff --git a/plugins/catalog-backend/src/next/stitching/Stitcher.test.ts b/plugins/catalog-backend/src/stitching/Stitcher.test.ts similarity index 100% rename from plugins/catalog-backend/src/next/stitching/Stitcher.test.ts rename to plugins/catalog-backend/src/stitching/Stitcher.test.ts diff --git a/plugins/catalog-backend/src/next/stitching/Stitcher.ts b/plugins/catalog-backend/src/stitching/Stitcher.ts similarity index 100% rename from plugins/catalog-backend/src/next/stitching/Stitcher.ts rename to plugins/catalog-backend/src/stitching/Stitcher.ts diff --git a/plugins/catalog-backend/src/next/stitching/buildEntitySearch.test.ts b/plugins/catalog-backend/src/stitching/buildEntitySearch.test.ts similarity index 100% rename from plugins/catalog-backend/src/next/stitching/buildEntitySearch.test.ts rename to plugins/catalog-backend/src/stitching/buildEntitySearch.test.ts diff --git a/plugins/catalog-backend/src/next/stitching/buildEntitySearch.ts b/plugins/catalog-backend/src/stitching/buildEntitySearch.ts similarity index 100% rename from plugins/catalog-backend/src/next/stitching/buildEntitySearch.ts rename to plugins/catalog-backend/src/stitching/buildEntitySearch.ts diff --git a/plugins/catalog-backend/src/next/stitching/index.ts b/plugins/catalog-backend/src/stitching/index.ts similarity index 100% rename from plugins/catalog-backend/src/next/stitching/index.ts rename to plugins/catalog-backend/src/stitching/index.ts diff --git a/plugins/catalog-backend/src/next/stitching/util.ts b/plugins/catalog-backend/src/stitching/util.ts similarity index 100% rename from plugins/catalog-backend/src/next/stitching/util.ts rename to plugins/catalog-backend/src/stitching/util.ts diff --git a/plugins/catalog-backend/src/next/util.ts b/plugins/catalog-backend/src/util/conversion.ts similarity index 100% rename from plugins/catalog-backend/src/next/util.ts rename to plugins/catalog-backend/src/util/conversion.ts diff --git a/plugins/catalog-backend/src/next/metrics.ts b/plugins/catalog-backend/src/util/metrics.ts similarity index 100% rename from plugins/catalog-backend/src/next/metrics.ts rename to plugins/catalog-backend/src/util/metrics.ts diff --git a/plugins/catalog-graph/api-report.md b/plugins/catalog-graph/api-report.md index b3694b9fe8..815f3427f2 100644 --- a/plugins/catalog-graph/api-report.md +++ b/plugins/catalog-graph/api-report.md @@ -93,13 +93,23 @@ export const EntityCatalogGraphCard: ({ }) => JSX.Element; // @public -export type EntityEdge = DependencyGraphTypes.DependencyEdge<{ +export type EntityEdge = DependencyGraphTypes.DependencyEdge; + +// Warning: (ae-missing-release-tag) "EntityEdgeData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type EntityEdgeData = { relations: string[]; label: 'visible'; -}>; +}; // @public -export type EntityNode = DependencyGraphTypes.DependencyNode<{ +export type EntityNode = DependencyGraphTypes.DependencyNode; + +// Warning: (ae-missing-release-tag) "EntityNodeData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type EntityNodeData = { name: string; kind?: string; title?: string; @@ -107,7 +117,7 @@ export type EntityNode = DependencyGraphTypes.DependencyNode<{ focused?: boolean; color?: 'primary' | 'secondary' | 'default'; onClick?: MouseEventHandler; -}>; +}; // @public export const EntityRelationsGraph: ({ diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.test.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.test.tsx index 2a48600941..5b8f50b983 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.test.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.test.tsx @@ -31,12 +31,6 @@ describe('', () => { relations: [RELATION_PARENT_OF], from: 'from-id', to: 'to-id', - id: 'id', - x: 111, - y: 222, - width: 100, - height: 25, - points: [], }} /> , @@ -54,12 +48,6 @@ describe('', () => { relations: [RELATION_PARENT_OF, RELATION_CHILD_OF], from: 'from-id', to: 'to-id', - id: 'id', - x: 111, - y: 222, - width: 100, - height: 25, - points: [], }} /> , diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.tsx index 9016891efa..60a858eee6 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.tsx @@ -17,7 +17,7 @@ import { DependencyGraphTypes } from '@backstage/core-components'; import { BackstageTheme } from '@backstage/theme'; import makeStyles from '@material-ui/core/styles/makeStyles'; import React from 'react'; -import { GraphEdge } from './types'; +import { EntityEdgeData } from './types'; import classNames from 'classnames'; const useStyles = makeStyles((theme: BackstageTheme) => ({ @@ -31,7 +31,7 @@ const useStyles = makeStyles((theme: BackstageTheme) => ({ export function CustomLabel({ edge: { relations }, -}: DependencyGraphTypes.RenderLabelProps) { +}: DependencyGraphTypes.RenderLabelProps) { const classes = useStyles(); return ( diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.test.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.test.tsx index f411f8255a..48272b8d1e 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.test.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.test.tsx @@ -36,10 +36,6 @@ describe('', () => { name: 'name', namespace: 'namespace', id: 'kind:namespace/name', - x: 111, - y: 222, - width: 100, - height: 25, color: 'primary', }} /> @@ -59,10 +55,6 @@ describe('', () => { name: 'name', namespace: 'default', id: 'kind:default/name', - x: 111, - y: 222, - width: 100, - height: 25, }} /> , @@ -83,10 +75,6 @@ describe('', () => { namespace: 'namespace', onClick, id: 'kind:namespace/name', - x: 111, - y: 222, - width: 100, - height: 25, }} /> , @@ -108,10 +96,6 @@ describe('', () => { namespace: 'namespace', title: 'Custom Title', id: 'kind:namespace/name', - x: 111, - y: 222, - width: 100, - height: 25, }} /> , diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.tsx index 53542cfbd6..9cd752a5d7 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.tsx @@ -20,7 +20,7 @@ import { makeStyles } from '@material-ui/core/styles'; import classNames from 'classnames'; import React, { useLayoutEffect, useRef, useState } from 'react'; import { EntityKindIcon } from './EntityKindIcon'; -import { GraphNode } from './types'; +import { EntityNodeData } from './types'; const useStyles = makeStyles((theme: BackstageTheme) => ({ node: { @@ -65,7 +65,7 @@ export function CustomNode({ title, onClick, }, -}: DependencyGraphTypes.RenderNodeProps) { +}: DependencyGraphTypes.RenderNodeProps) { const classes = useStyles(); const [width, setWidth] = useState(0); const [height, setHeight] = useState(0); diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/index.ts b/plugins/catalog-graph/src/components/EntityRelationsGraph/index.ts index 63ac9c2eac..b86c8f05ae 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/index.ts +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/index.ts @@ -17,4 +17,9 @@ export { EntityRelationsGraph } from './EntityRelationsGraph'; export { ALL_RELATION_PAIRS } from './relations'; export type { RelationPairs } from './relations'; export { Direction } from './types'; -export type { EntityEdge, EntityNode } from './types'; +export type { + EntityEdgeData, + EntityEdge, + EntityNodeData, + EntityNode, +} from './types'; diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/types.ts b/plugins/catalog-graph/src/components/EntityRelationsGraph/types.ts index d6bda71a33..830032d527 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/types.ts +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/types.ts @@ -17,11 +17,9 @@ import { DependencyGraphTypes } from '@backstage/core-components'; import { MouseEventHandler } from 'react'; /** - * Edge between two entities. - * - * @public + * Additional Data for entities */ -export type EntityEdge = DependencyGraphTypes.DependencyEdge<{ +export type EntityEdgeData = { /** * Up to two relations that are connecting an entity. */ @@ -31,14 +29,19 @@ export type EntityEdge = DependencyGraphTypes.DependencyEdge<{ */ // Not used, but has to be non empty to draw a label at all! label: 'visible'; -}>; +}; /** - * Node representing an entity. + * Edge between two entities. * * @public */ -export type EntityNode = DependencyGraphTypes.DependencyNode<{ +export type EntityEdge = DependencyGraphTypes.DependencyEdge; + +/** + * Additional data for Entity Node + */ +export type EntityNodeData = { /** * Name of the entity. */ @@ -68,11 +71,14 @@ export type EntityNode = DependencyGraphTypes.DependencyNode<{ * Optional click handler. */ onClick?: MouseEventHandler; -}>; +}; -export type GraphEdge = DependencyGraphTypes.GraphEdge; - -export type GraphNode = DependencyGraphTypes.GraphNode; +/** + * Node representing an entity. + * + * @public + */ +export type EntityNode = DependencyGraphTypes.DependencyNode; /** * Render direction of the graph. diff --git a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewCatalogInfoComponent.test.tsx b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewCatalogInfoComponent.test.tsx index f0844c265c..2618fab57a 100644 --- a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewCatalogInfoComponent.test.tsx +++ b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewCatalogInfoComponent.test.tsx @@ -46,7 +46,7 @@ const entities: Entity[] = [ describe('', () => { it('renders without exploding', async () => { - const { getByText } = render( + const { getByText, findByText } = render( ', () => { ); const repositoryUrl = getByText('http://my-repository/a/catalog-info.yaml'); - const kindText = getByText('Kind_2'); + const kindText = await findByText('Kind_2'); expect(repositoryUrl).toBeInTheDocument(); expect(repositoryUrl).toBeVisible(); expect(kindText).toBeInTheDocument(); diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index 7f50e87e03..ce7282276e 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -257,13 +257,14 @@ export const EntityRefLink: React_2.ForwardRefExoticComponent< | 'children' | 'key' | 'id' + | 'className' | 'classes' + | 'innerRef' | 'defaultChecked' | 'defaultValue' | 'suppressContentEditableWarning' | 'suppressHydrationWarning' | 'accessKey' - | 'className' | 'contentEditable' | 'contextMenu' | 'draggable' @@ -504,7 +505,6 @@ export const EntityRefLink: React_2.ForwardRefExoticComponent< | 'onTransitionEndCapture' | 'component' | 'variant' - | 'innerRef' | 'download' | 'href' | 'hrefLang' diff --git a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx index 628526e9ab..750692ed2e 100644 --- a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx +++ b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx @@ -19,6 +19,7 @@ import { Box, Checkbox, FormControlLabel, + makeStyles, TextField, Typography, } from '@material-ui/core'; @@ -30,10 +31,20 @@ import React, { useEffect, useMemo, useState } from 'react'; import { useEntityListProvider } from '../../hooks/useEntityListProvider'; import { EntityLifecycleFilter } from '../../filters'; +const useStyles = makeStyles( + { + input: {}, + }, + { + name: 'CatalogReactEntityLifecyclePicker', + }, +); + const icon = ; const checkedIcon = ; export const EntityLifecyclePicker = () => { + const classes = useStyles(); const { updateFilters, backendEntities, filters, queryParameters } = useEntityListProvider(); @@ -91,7 +102,9 @@ export const EntityLifecyclePicker = () => { )} size="small" popupIcon={} - renderInput={params => } + renderInput={params => ( + + )} /> ); diff --git a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx index 49c950f57e..dbf6a33164 100644 --- a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx +++ b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx @@ -19,6 +19,7 @@ import { Box, Checkbox, FormControlLabel, + makeStyles, TextField, Typography, } from '@material-ui/core'; @@ -32,10 +33,20 @@ import { EntityOwnerFilter } from '../../filters'; import { getEntityRelations } from '../../utils'; import { formatEntityRefTitle } from '../EntityRefLink'; +const useStyles = makeStyles( + { + input: {}, + }, + { + name: 'CatalogReactEntityOwnerPicker', + }, +); + const icon = ; const checkedIcon = ; export const EntityOwnerPicker = () => { + const classes = useStyles(); const { updateFilters, backendEntities, filters, queryParameters } = useEntityListProvider(); @@ -95,7 +106,9 @@ export const EntityOwnerPicker = () => { )} size="small" popupIcon={} - renderInput={params => } + renderInput={params => ( + + )} /> ); diff --git a/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.tsx b/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.tsx index 9fd30b5182..3a848591a3 100644 --- a/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.tsx +++ b/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.tsx @@ -29,15 +29,21 @@ import { useDebounce } from 'react-use'; import { useEntityListProvider } from '../../hooks/useEntityListProvider'; import { EntityTextFilter } from '../../filters'; -const useStyles = makeStyles(_theme => ({ - searchToolbar: { - paddingLeft: 0, - paddingRight: 0, +const useStyles = makeStyles( + _theme => ({ + searchToolbar: { + paddingLeft: 0, + paddingRight: 0, + }, + input: {}, + }), + { + name: 'CatalogReactEntitySearchBar', }, -})); +); export const EntitySearchBar = () => { - const styles = useStyles(); + const classes = useStyles(); const { filters, updateFilters } = useEntityListProvider(); const [search, setSearch] = useState(filters.text?.value ?? ''); @@ -53,10 +59,11 @@ export const EntitySearchBar = () => { ); return ( - + setSearch(event.target.value)} diff --git a/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.tsx b/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.tsx index 64c80d1cde..9e00f7e7b0 100644 --- a/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.tsx +++ b/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.tsx @@ -19,6 +19,7 @@ import { Box, Checkbox, FormControlLabel, + makeStyles, TextField, Typography, } from '@material-ui/core'; @@ -30,10 +31,20 @@ import React, { useEffect, useMemo, useState } from 'react'; import { useEntityListProvider } from '../../hooks/useEntityListProvider'; import { EntityTagFilter } from '../../filters'; +const useStyles = makeStyles( + { + input: {}, + }, + { + name: 'CatalogReactEntityTagPicker', + }, +); + const icon = ; const checkedIcon = ; export const EntityTagPicker = () => { + const classes = useStyles(); const { updateFilters, backendEntities, filters, queryParameters } = useEntityListProvider(); @@ -87,7 +98,9 @@ export const EntityTagPicker = () => { )} size="small" popupIcon={} - renderInput={params => } + renderInput={params => ( + + )} /> ); diff --git a/plugins/catalog-react/src/components/UserListPicker/UserListPicker.tsx b/plugins/catalog-react/src/components/UserListPicker/UserListPicker.tsx index bfc55142a6..f861355762 100644 --- a/plugins/catalog-react/src/components/UserListPicker/UserListPicker.tsx +++ b/plugins/catalog-react/src/components/UserListPicker/UserListPicker.tsx @@ -43,29 +43,34 @@ import { import { UserListFilterKind } from '../../types'; import { reduceEntityFilters } from '../../utils'; -const useStyles = makeStyles(theme => ({ - root: { - backgroundColor: 'rgba(0, 0, 0, .11)', - boxShadow: 'none', - margin: theme.spacing(1, 0, 1, 0), +const useStyles = makeStyles( + theme => ({ + root: { + backgroundColor: 'rgba(0, 0, 0, .11)', + boxShadow: 'none', + margin: theme.spacing(1, 0, 1, 0), + }, + title: { + margin: theme.spacing(1, 0, 0, 1), + textTransform: 'uppercase', + fontSize: 12, + fontWeight: 'bold', + }, + listIcon: { + minWidth: 30, + color: theme.palette.text.primary, + }, + menuItem: { + minHeight: theme.spacing(6), + }, + groupWrapper: { + margin: theme.spacing(1, 1, 2, 1), + }, + }), + { + name: 'CatalogReactUserListPicker', }, - title: { - margin: theme.spacing(1, 0, 0, 1), - textTransform: 'uppercase', - fontSize: 12, - fontWeight: 'bold', - }, - listIcon: { - minWidth: 30, - color: theme.palette.text.primary, - }, - menuItem: { - minHeight: theme.spacing(6), - }, - groupWrapper: { - margin: theme.spacing(1, 1, 2, 1), - }, -})); +); export type ButtonGroup = { name: string; diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index 4937a3eb86..2be7fc8575 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -23,10 +23,12 @@ import { IconComponent } from '@backstage/core-plugin-api'; import { IdentityApi } from '@backstage/core-plugin-api'; import { InfoCardVariants } from '@backstage/core-components'; import { Location as Location_2 } from '@backstage/catalog-model'; +import { Overrides } from '@material-ui/core/styles/overrides'; import { PropsWithChildren } from 'react'; import { default as React_2 } from 'react'; import { ReactNode } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; +import { StyleRules } from '@material-ui/core/styles/withStyles'; import { TableColumn } from '@backstage/core-components'; import { TableProps } from '@backstage/core-components'; import { TabProps } from '@material-ui/core'; @@ -55,6 +57,13 @@ export const AboutField: ({ children, }: Props_2) => JSX.Element; +// @public (undocumented) +export type BackstageOverrides = Overrides & { + [Name in keyof PluginCatalogComponentsNameToClassKey]?: Partial< + StyleRules + >; +}; + // Warning: (ae-missing-release-tag) "CatalogClientWrapper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @@ -323,6 +332,9 @@ export const EntityLinksCard: ({ variant?: 'gridItem' | undefined; }) => JSX.Element; +// @public (undocumented) +export type EntityLinksEmptyStateClassKey = 'code'; + // Warning: (ae-missing-release-tag) "EntityListContainer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -424,6 +436,12 @@ export function isNamespace(namespace: string): (entity: Entity) => boolean; // @public (undocumented) export const isOrphan: (entity: Entity) => boolean; +// @public (undocumented) +export type PluginCatalogComponentsNameToClassKey = { + PluginCatalogEntityLinksEmptyState: EntityLinksEmptyStateClassKey; + PluginCatalogSystemDiagramCard: SystemDiagramCardClassKey; +}; + // Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @deprecated (undocumented) @@ -433,6 +451,14 @@ export const Router: ({ EntityPage?: React_2.ComponentType<{}> | undefined; }) => JSX.Element; +// @public (undocumented) +export type SystemDiagramCardClassKey = + | 'domainNode' + | 'systemNode' + | 'componentNode' + | 'apiNode' + | 'resourceNode'; + // Warnings were encountered during analysis: // // src/components/CatalogTable/CatalogTable.d.ts:10:5 - (ae-forgotten-export) The symbol "CatalogTableProps" needs to be exported by the entry point index.d.ts diff --git a/plugins/catalog/src/components/EntityLinksCard/EntityLinksEmptyState.tsx b/plugins/catalog/src/components/EntityLinksCard/EntityLinksEmptyState.tsx index b0e8ee5d74..54325ffb5d 100644 --- a/plugins/catalog/src/components/EntityLinksCard/EntityLinksEmptyState.tsx +++ b/plugins/catalog/src/components/EntityLinksCard/EntityLinksEmptyState.tsx @@ -26,13 +26,19 @@ const ENTITY_YAML = `metadata: title: My Dashboard icon: dashboard`; -const useStyles = makeStyles(theme => ({ - code: { - borderRadius: 6, - margin: `${theme.spacing(2)}px 0px`, - background: theme.palette.type === 'dark' ? '#444' : '#fff', - }, -})); +/** @public */ +export type EntityLinksEmptyStateClassKey = 'code'; + +const useStyles = makeStyles( + theme => ({ + code: { + borderRadius: 6, + margin: `${theme.spacing(2)}px 0px`, + background: theme.palette.type === 'dark' ? '#444' : '#fff', + }, + }), + { name: 'PluginCatalogEntityLinksEmptyState' }, +); export const EntityLinksEmptyState = () => { const classes = useStyles(); diff --git a/plugins/catalog/src/components/EntityLinksCard/index.ts b/plugins/catalog/src/components/EntityLinksCard/index.ts index 04c30c0f68..fc53904a47 100644 --- a/plugins/catalog/src/components/EntityLinksCard/index.ts +++ b/plugins/catalog/src/components/EntityLinksCard/index.ts @@ -15,3 +15,4 @@ */ export { EntityLinksCard } from './EntityLinksCard'; +export type { EntityLinksEmptyStateClassKey } from './EntityLinksEmptyState'; diff --git a/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx b/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx index b3326abaab..984ae9f982 100644 --- a/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx +++ b/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx @@ -46,28 +46,39 @@ import { import { useApi, useRouteRef } from '@backstage/core-plugin-api'; -const useStyles = makeStyles((theme: BackstageTheme) => ({ - domainNode: { - fill: theme.palette.primary.main, - stroke: theme.palette.border, - }, - systemNode: { - fill: 'coral', - stroke: theme.palette.border, - }, - componentNode: { - fill: 'yellowgreen', - stroke: theme.palette.border, - }, - apiNode: { - fill: theme.palette.gold, - stroke: theme.palette.border, - }, - resourceNode: { - fill: 'grey', - stroke: theme.palette.border, - }, -})); +/** @public */ +export type SystemDiagramCardClassKey = + | 'domainNode' + | 'systemNode' + | 'componentNode' + | 'apiNode' + | 'resourceNode'; + +const useStyles = makeStyles( + (theme: BackstageTheme) => ({ + domainNode: { + fill: theme.palette.primary.main, + stroke: theme.palette.border, + }, + systemNode: { + fill: 'coral', + stroke: theme.palette.border, + }, + componentNode: { + fill: 'yellowgreen', + stroke: theme.palette.border, + }, + apiNode: { + fill: theme.palette.gold, + stroke: theme.palette.border, + }, + resourceNode: { + fill: 'grey', + stroke: theme.palette.border, + }, + }), + { name: 'PluginCatalogSystemDiagramCard' }, +); // Simplifies the diagram output by hiding the default namespace and kind function readableEntityName( diff --git a/plugins/catalog/src/components/SystemDiagramCard/index.ts b/plugins/catalog/src/components/SystemDiagramCard/index.ts index 4c1427c838..bc9ed6309d 100644 --- a/plugins/catalog/src/components/SystemDiagramCard/index.ts +++ b/plugins/catalog/src/components/SystemDiagramCard/index.ts @@ -15,3 +15,4 @@ */ export { SystemDiagramCard } from './SystemDiagramCard'; +export type { SystemDiagramCardClassKey } from './SystemDiagramCard'; diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts index 54f4da5e7e..d804bbaf83 100644 --- a/plugins/catalog/src/index.ts +++ b/plugins/catalog/src/index.ts @@ -33,6 +33,7 @@ export * from './components/EntityProcessingErrorsPanel'; export * from './components/EntityPageLayout'; export * from './components/EntitySwitch'; export * from './components/FilteredEntityLayout'; +export * from './overridableComponents'; export { Router } from './components/Router'; export { CatalogEntityPage, @@ -50,3 +51,6 @@ export { EntityLinksCard, EntitySystemDiagramCard, } from './plugin'; + +export type { EntityLinksEmptyStateClassKey } from './components/EntityLinksCard'; +export type { SystemDiagramCardClassKey } from './components/SystemDiagramCard'; diff --git a/plugins/catalog/src/overridableComponents.ts b/plugins/catalog/src/overridableComponents.ts new file mode 100644 index 0000000000..e76aafbb68 --- /dev/null +++ b/plugins/catalog/src/overridableComponents.ts @@ -0,0 +1,33 @@ +/* + * 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. + */ +import { Overrides } from '@material-ui/core/styles/overrides'; +import { StyleRules } from '@material-ui/core/styles/withStyles'; + +import { EntityLinksEmptyStateClassKey } from './components/EntityLinksCard'; +import { SystemDiagramCardClassKey } from './components/SystemDiagramCard'; + +/** @public */ +export type PluginCatalogComponentsNameToClassKey = { + PluginCatalogEntityLinksEmptyState: EntityLinksEmptyStateClassKey; + PluginCatalogSystemDiagramCard: SystemDiagramCardClassKey; +}; + +/** @public */ +export type BackstageOverrides = Overrides & { + [Name in keyof PluginCatalogComponentsNameToClassKey]?: Partial< + StyleRules + >; +}; diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index db7631290d..a1cd98124a 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -122,6 +122,9 @@ export const SettingsModal: ({ children: JSX.Element; }) => JSX.Element; +// @public +export const WelcomeTitle: () => JSX.Element; + // Warnings were encountered during analysis: // // src/extensions.d.ts:16:5 - (ae-forgotten-export) The symbol "ComponentParts" needs to be exported by the entry point index.d.ts diff --git a/plugins/home/src/componentRenderers/ComponentTabs/ComponentTabs.test.tsx b/plugins/home/src/componentRenderers/ComponentTabs/ComponentTabs.test.tsx new file mode 100644 index 0000000000..96ab37a8aa --- /dev/null +++ b/plugins/home/src/componentRenderers/ComponentTabs/ComponentTabs.test.tsx @@ -0,0 +1,76 @@ +/* + * 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. + */ + +import { render } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; +import { ComponentTabs } from './ComponentTabs'; + +describe('', () => { + test('should render tabs without exploding', () => { + const { getByText } = render( + <>ContentA, + }, + { + label: 'TabB', + Component: () => <>ContentB, + }, + ]} + />, + ); + + expect(getByText('TabA')).toBeInTheDocument(); + expect(getByText('TabB')).toBeInTheDocument(); + + expect(getByText('ContentA')).toBeInTheDocument(); + expect(getByText('ContentB')).toHaveStyle({ + display: 'none', + }); + }); + + test('should switch tab on click', () => { + const { getByText } = render( + <>ContentA, + }, + { + label: 'TabB', + Component: () => <>ContentB, + }, + ]} + />, + ); + + expect(getByText('ContentB')).toHaveStyle({ + display: 'none', + }); + + userEvent.click(getByText('TabB')); + + expect(getByText('ContentA')).toHaveStyle({ + display: 'none', + }); + }); +}); diff --git a/plugins/home/src/componentRenderers/ComponentTabs/ComponentTabs.tsx b/plugins/home/src/componentRenderers/ComponentTabs/ComponentTabs.tsx index f404a80980..d6d3561fe1 100644 --- a/plugins/home/src/componentRenderers/ComponentTabs/ComponentTabs.tsx +++ b/plugins/home/src/componentRenderers/ComponentTabs/ComponentTabs.tsx @@ -44,7 +44,10 @@ export const ComponentTabs = ({ ))} {tabs.map(({ Component }, idx) => ( -
+
))} diff --git a/plugins/home/src/homePageComponents/WelcomeTitle/WelcomeTitle.test.tsx b/plugins/home/src/homePageComponents/WelcomeTitle/WelcomeTitle.test.tsx new file mode 100644 index 0000000000..839ecdbae1 --- /dev/null +++ b/plugins/home/src/homePageComponents/WelcomeTitle/WelcomeTitle.test.tsx @@ -0,0 +1,32 @@ +/* + * 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. + */ +import { renderInTestApp } from '@backstage/test-utils'; +import React from 'react'; +import { WelcomeTitle } from './WelcomeTitle'; + +describe('', () => { + afterEach(() => jest.resetAllMocks()); + + test('should greet user', async () => { + jest + .spyOn(global.Date, 'now') + .mockImplementation(() => new Date('1970-01-01T23:00:00').valueOf()); + + const { getByText } = await renderInTestApp(); + + expect(getByText(/Get some rest, Guest/)).toBeInTheDocument(); + }); +}); diff --git a/plugins/home/src/homePageComponents/WelcomeTitle/WelcomeTitle.tsx b/plugins/home/src/homePageComponents/WelcomeTitle/WelcomeTitle.tsx new file mode 100644 index 0000000000..04ec7e1673 --- /dev/null +++ b/plugins/home/src/homePageComponents/WelcomeTitle/WelcomeTitle.tsx @@ -0,0 +1,32 @@ +/* + * 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. + */ +import { identityApiRef, useApi } from '@backstage/core-plugin-api'; +import { Tooltip } from '@material-ui/core'; +import React, { useMemo } from 'react'; +import { getTimeBasedGreeting } from './timeUtil'; + +export const WelcomeTitle = () => { + const identityApi = useApi(identityApiRef); + const profile = identityApi.getProfile(); + const userId = identityApi.getUserId(); + const greeting = useMemo(() => getTimeBasedGreeting(), []); + + return ( + + {`${greeting.greeting}, ${profile.displayName || userId}!`} + + ); +}; diff --git a/plugins/catalog-backend/src/next/Context/index.ts b/plugins/home/src/homePageComponents/WelcomeTitle/index.ts similarity index 71% rename from plugins/catalog-backend/src/next/Context/index.ts rename to plugins/home/src/homePageComponents/WelcomeTitle/index.ts index 822edba478..ca237511e7 100644 --- a/plugins/catalog-backend/src/next/Context/index.ts +++ b/plugins/home/src/homePageComponents/WelcomeTitle/index.ts @@ -13,9 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -export { BackgroundContext } from './BackgroundContext'; -export { ContextWithValue } from './ContextWithValue'; -export { TransactionValue } from './TransactionValue'; -export { ContextKey } from './types'; -export type { Context } from './types'; +export { WelcomeTitle } from './WelcomeTitle'; diff --git a/plugins/welcome/src/utils/locales/goodAfternoon.locales.json b/plugins/home/src/homePageComponents/WelcomeTitle/locales/goodAfternoon.locales.json similarity index 100% rename from plugins/welcome/src/utils/locales/goodAfternoon.locales.json rename to plugins/home/src/homePageComponents/WelcomeTitle/locales/goodAfternoon.locales.json diff --git a/plugins/welcome/src/utils/locales/goodEvening.locales.json b/plugins/home/src/homePageComponents/WelcomeTitle/locales/goodEvening.locales.json similarity index 100% rename from plugins/welcome/src/utils/locales/goodEvening.locales.json rename to plugins/home/src/homePageComponents/WelcomeTitle/locales/goodEvening.locales.json diff --git a/plugins/welcome/src/utils/locales/goodMorning.locales.json b/plugins/home/src/homePageComponents/WelcomeTitle/locales/goodMorning.locales.json similarity index 100% rename from plugins/welcome/src/utils/locales/goodMorning.locales.json rename to plugins/home/src/homePageComponents/WelcomeTitle/locales/goodMorning.locales.json diff --git a/plugins/welcome/src/utils/timeUtil.test.js b/plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.test.ts similarity index 55% rename from plugins/welcome/src/utils/timeUtil.test.js rename to plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.test.ts index 2abb03152f..77eb6677ca 100644 --- a/plugins/welcome/src/utils/timeUtil.test.js +++ b/plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.test.ts @@ -16,16 +16,20 @@ import { getTimeBasedGreeting } from './timeUtil'; -it('has greeting and language', () => { - const greeting = getTimeBasedGreeting(); - expect(greeting).toHaveProperty('greeting'); - expect(greeting).toHaveProperty('language'); -}); +describe('getTimeBasedGreeting', () => { + afterEach(() => jest.resetAllMocks()); -it('greets late at night', () => { - jest - .spyOn(global.Date, 'now') - .mockImplementationOnce(() => new Date('1970-01-01T23:00:00').valueOf()); - const greeting = getTimeBasedGreeting(); - expect(greeting.greeting).toBe('Get some rest'); + it('has greeting and language', () => { + const greeting = getTimeBasedGreeting(); + expect(greeting).toHaveProperty('greeting'); + expect(greeting).toHaveProperty('language'); + }); + + it('greets late at night', () => { + jest + .spyOn(global.Date, 'now') + .mockImplementationOnce(() => new Date('1970-01-01T23:00:00').valueOf()); + const greeting = getTimeBasedGreeting(); + expect(greeting.greeting).toBe('Get some rest'); + }); }); diff --git a/plugins/welcome/src/utils/timeUtil.js b/plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.ts similarity index 83% rename from plugins/welcome/src/utils/timeUtil.js rename to plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.ts index 93f4379d8e..8df7703b44 100644 --- a/plugins/welcome/src/utils/timeUtil.js +++ b/plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.ts @@ -18,12 +18,12 @@ import goodMorning from './locales/goodMorning.locales.json'; import goodAfternoon from './locales/goodAfternoon.locales.json'; import goodEvening from './locales/goodEvening.locales.json'; -// Select a large random integer at startup, to prevent the greetings to change every time the user -// navigates. +// Select a large random integer at startup, to prevent the greetings to change +// every time the user navigates. const greetingRandomSeed = Math.floor(Math.random() * 1000000); -export function getTimeBasedGreeting() { - const random = array => array[greetingRandomSeed % array.length]; +export function getTimeBasedGreeting(): { language: string; greeting: string } { + const random = (array: string[]) => array[greetingRandomSeed % array.length]; const currentHour = new Date(Date.now()).getHours(); if (currentHour >= 23) { @@ -32,7 +32,7 @@ export function getTimeBasedGreeting() { greeting: 'Get some rest', }; } - const timeOfDay = hour => { + const timeOfDay = (hour: number): { [language: string]: string } => { if (hour < 12) return goodMorning; if (hour < 17) return goodAfternoon; return goodEvening; diff --git a/plugins/home/src/index.ts b/plugins/home/src/index.ts index 0dacda7a01..0004cf5a23 100644 --- a/plugins/home/src/index.ts +++ b/plugins/home/src/index.ts @@ -27,6 +27,7 @@ export { ComponentAccordion, ComponentTabs, ComponentTab, + WelcomeTitle, } from './plugin'; export { SettingsModal } from './components'; export { createCardExtension } from './extensions'; diff --git a/plugins/home/src/plugin.ts b/plugins/home/src/plugin.ts index 22ece00362..d67ea846a5 100644 --- a/plugins/home/src/plugin.ts +++ b/plugins/home/src/plugin.ts @@ -64,6 +64,20 @@ export const ComponentTab = homePlugin.provide( }), ); +/** + * A component to display a playful greeting for the user. + * + * @public + */ +export const WelcomeTitle = homePlugin.provide( + createComponentExtension({ + component: { + lazy: () => + import('./homePageComponents/WelcomeTitle').then(m => m.WelcomeTitle), + }, + }), +); + export const HomePageRandomJoke = homePlugin.provide( createCardExtension<{ defaultCategory?: 'any' | 'programming' }>({ title: 'Random Joke', diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts index d676af56ca..4befec5a81 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts @@ -15,10 +15,14 @@ */ import os from 'os'; -import { join as joinPath, resolve as resolvePath } from 'path'; +import { join as joinPath } from 'path'; import fs from 'fs-extra'; import mockFs from 'mock-fs'; -import { getVoidLogger, UrlReader } from '@backstage/backend-common'; +import { + getVoidLogger, + resolvePackagePath, + UrlReader, +} from '@backstage/backend-common'; import { ScmIntegrations } from '@backstage/integration'; import { PassThrough } from 'stream'; import { fetchContents } from './helpers'; @@ -30,9 +34,9 @@ jest.mock('./helpers', () => ({ })); const aBinaryFile = fs.readFileSync( - resolvePath( - 'src', - '../fixtures/test-nested-template/public/react-logo192.png', + resolvePackagePath( + '@backstage/plugin-scaffolder-backend', + 'fixtures/test-nested-template/public/react-logo192.png', ), ); diff --git a/plugins/search/api-report.md b/plugins/search/api-report.md index 169972d02d..b29149805c 100644 --- a/plugins/search/api-report.md +++ b/plugins/search/api-report.md @@ -79,7 +79,11 @@ export const searchApiRef: ApiRef; // Warning: (ae-missing-release-tag) "SearchBar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const SearchBar: ({ className, debounceTime }: Props) => JSX.Element; +export const SearchBar: ({ + className, + debounceTime, + placeholder, +}: Props) => JSX.Element; // Warning: (ae-missing-release-tag) "SearchBarNext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -87,9 +91,11 @@ export const SearchBar: ({ className, debounceTime }: Props) => JSX.Element; export const SearchBarNext: ({ className, debounceTime, + placeholder, }: { className?: string | undefined; debounceTime?: number | undefined; + placeholder?: string | 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) diff --git a/plugins/search/src/components/SearchBar/SearchBar.stories.tsx b/plugins/search/src/components/SearchBar/SearchBar.stories.tsx index fa2ba0d94e..2c990e512c 100644 --- a/plugins/search/src/components/SearchBar/SearchBar.stories.tsx +++ b/plugins/search/src/components/SearchBar/SearchBar.stories.tsx @@ -45,3 +45,20 @@ export const Default = () => { ); }; + +export const CustomPlaceholder = () => { + return ( + + {/* @ts-ignore (defaultValue requires more than what is used here) */} + + + + + + + + + + + ); +}; diff --git a/plugins/search/src/components/SearchBar/SearchBar.test.tsx b/plugins/search/src/components/SearchBar/SearchBar.test.tsx index de8ea9a5a7..3a11daa9fa 100644 --- a/plugins/search/src/components/SearchBar/SearchBar.test.tsx +++ b/plugins/search/src/components/SearchBar/SearchBar.test.tsx @@ -65,6 +65,26 @@ describe('SearchBar', () => { await waitFor(() => { expect(screen.getByRole('textbox', { name })).toBeInTheDocument(); + expect( + screen.getByPlaceholderText('Search in Mock title'), + ).toBeInTheDocument(); + }); + }); + + it('Renders with custom placeholder', async () => { + render( + + + + + , + , + ); + + await waitFor(() => { + expect( + screen.getByPlaceholderText('This is a custom placeholder'), + ).toBeInTheDocument(); }); }); diff --git a/plugins/search/src/components/SearchBar/SearchBar.tsx b/plugins/search/src/components/SearchBar/SearchBar.tsx index 214e22520c..53802899eb 100644 --- a/plugins/search/src/components/SearchBar/SearchBar.tsx +++ b/plugins/search/src/components/SearchBar/SearchBar.tsx @@ -89,9 +89,14 @@ export const SearchBarBase = ({ type Props = { className?: string; debounceTime?: number; + placeholder?: string; }; -export const SearchBar = ({ className, debounceTime = 0 }: Props) => { +export const SearchBar = ({ + className, + debounceTime = 0, + placeholder, +}: Props) => { const { term, setTerm } = useSearch(); const [value, setValue] = useState(term); @@ -113,6 +118,7 @@ export const SearchBar = ({ className, debounceTime = 0 }: Props) => { value={value} onChange={handleQuery} onClear={handleClear} + placeholder={placeholder} /> ); }; diff --git a/yarn.lock b/yarn.lock index eba5fb856f..1a0caad7e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2105,7 +2105,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.2.0": +"@babel/runtime@^7.12.1", "@babel/runtime@^7.2.0": version "7.15.4" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== @@ -3686,18 +3686,6 @@ underscore "^1.9.1" ws "^7.3.1" -"@kyleshockey/object-assign-deep@^0.4.2": - version "0.4.2" - resolved "https://registry.npmjs.org/@kyleshockey/object-assign-deep/-/object-assign-deep-0.4.2.tgz#84900f0eefc372798f4751b5262830b8208922ec" - integrity sha1-hJAPDu/DcnmPR1G1JigwuCCJIuw= - -"@kyleshockey/xml@^1.0.2": - version "1.0.2" - resolved "https://registry.npmjs.org/@kyleshockey/xml/-/xml-1.0.2.tgz#81fad3d7c33da2ba2639db095db3db24c2921f70" - integrity sha512-iMo32MPLcI9cPxs3YL5kmKxKgDmkSZDCFEqIT5eRk7d/Ll8r4X3SwGYSigzALd6+RHWlFEmjL1QyaQ15xDZFlw== - dependencies: - stream "^0.0.2" - "@lerna/add@4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" @@ -4960,11 +4948,6 @@ "@octokit/types" "^6.12.2" btoa-lite "^1.0.0" -"@octokit/openapi-types@^2.2.0": - version "2.2.0" - resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-2.2.0.tgz#123e0438a0bc718ccdac3b5a2e69b3dd00daa85b" - integrity sha512-274lNUDonw10kT8wHg8fCcUc1ZjZHbWv0/TbAwb0ojhBQqZYc1cQ/4yqTVTtPMDeZ//g7xVEYe/s3vURkRghPg== - "@octokit/openapi-types@^7.3.2": version "7.3.2" resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-7.3.2.tgz#065ce49b338043ec7f741316ce06afd4d459d944" @@ -6807,6 +6790,14 @@ dependencies: highlight.js "^10.1.0" +"@types/hoist-non-react-statics@^3.3.0": + version "3.3.1" + resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" + integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/html-minifier-terser@^5.0.0": version "5.1.0" resolved "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz#551a4589b6ee2cc9c1dff08056128aec29b94880" @@ -7315,6 +7306,16 @@ "@types/react" "*" immutable ">=3.8.2" +"@types/react-redux@^7.1.16": + version "7.1.19" + resolved "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.19.tgz#477bd0a9b01bae6d6bf809418cdfa7d3c16d4c62" + integrity sha512-L37dSCT0aoJnCgpR8Iuginlbxoh7qhWOXiaDqEsxVMrER1CmVhFD+63NxgJeT4pkmEM28oX0NH4S4f+sXHTZjA== + dependencies: + "@types/hoist-non-react-statics" "^3.3.0" + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + redux "^4.0.0" + "@types/react-sparklines@^1.7.0": version "1.7.0" resolved "https://registry.npmjs.org/@types/react-sparklines/-/react-sparklines-1.7.0.tgz#f956d0f7b0e746ad445ce1cd250fe81f8a384684" @@ -7373,13 +7374,6 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/react@16.4.6": - version "16.4.6" - resolved "https://registry.npmjs.org/@types/react/-/react-16.4.6.tgz#5024957c6bcef4f02823accf5974faba2e54fada" - integrity sha512-9LDZdhsuKSc+DjY65SjBkA958oBWcTWSVWAd2cD9XqKBjhGw1KzAkRhWRw2eIsXvaIE/TOTjjKMFVC+JA1iU4g== - dependencies: - csstype "^2.2.0" - "@types/recharts@^1.8.14", "@types/recharts@^1.8.15": version "1.8.19" resolved "https://registry.npmjs.org/@types/recharts/-/recharts-1.8.19.tgz#047f72cf4c25df545aa1085fe3a085e58a2483c1" @@ -11431,14 +11425,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -create-react-class@^15.5.1: - version "15.7.0" - resolved "https://registry.npmjs.org/create-react-class/-/create-react-class-15.7.0.tgz#7499d7ca2e69bb51d13faf59bd04f0c65a1d6c1e" - integrity sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng== - dependencies: - loose-envify "^1.3.1" - object-assign "^4.1.1" - create-react-context@0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz#546dede9dc422def0d3fc2fe03afe0bc0f4f7d8c" @@ -11795,7 +11781,7 @@ cssstyle@^2.2.0: dependencies: cssom "~0.3.6" -csstype@^2.2.0, csstype@^2.5.2, csstype@^2.5.7, csstype@^2.6.7: +csstype@^2.5.2, csstype@^2.5.7, csstype@^2.6.7: version "2.6.17" resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz#4cf30eb87e1d1a005d8b6510f95292413f6a1c0e" integrity sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A== @@ -12877,11 +12863,6 @@ elliptic@^6.0.0: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -emitter-component@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz#065e2dbed6959bf470679edabeaf7981d1003ab6" - integrity sha1-Bl4tvtaVm/RwZ57avq95gdEAOrY= - emittery@^0.7.1: version "0.7.1" resolved "https://registry.npmjs.org/emittery/-/emittery-0.7.1.tgz#c02375a927a40948c0345cc903072597f5270451" @@ -13988,11 +13969,6 @@ fecha@^4.2.0: resolved "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41" integrity sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg== -fetch-blob@2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" - integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== - fetch-readablestream@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/fetch-readablestream/-/fetch-readablestream-0.2.0.tgz#eaa6d1a76b12de2d4731a343393c6ccdcfe2c795" @@ -14305,10 +14281,10 @@ fork-ts-checker-webpack-plugin@^6.0.4: semver "^7.3.2" tapable "^1.0.0" -form-data-encoder@1.4.3, form-data-encoder@^1.0.1: - version "1.4.3" - resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.4.3.tgz#b0975dbe5795676f211c0ff98e473eaf52b0db4a" - integrity sha512-ARLR/jJaj3+tlKkO7h1uvvjQcD6xCiKyg42hcG5Q4jv8uDa1IMPs81bM3BwI8BrqVEQxF9pX6tx0iLIzAvr31Q== +form-data-encoder@^1.4.3: + version "1.6.0" + resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.6.0.tgz#9dd1f479836c1b1b47201667c68f8daafa800943" + integrity sha512-P97AVaOB8hZaniiKK3f46zxQcchQXI8EgBnX+2+719gLv5ZbDSf3J1XtIuAQ8xbGLU4vZYhy7xwhFtK8U5u9Nw== form-data@4.0.0, form-data@^4.0.0: version "4.0.0" @@ -14351,14 +14327,13 @@ format@^0.2.0: resolved "https://registry.npmjs.org/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs= -formdata-node@^3.6.2: - version "3.7.0" - resolved "https://registry.npmjs.org/formdata-node/-/formdata-node-3.7.0.tgz#b58942e617639a0ebea57227cfff8332a4020760" - integrity sha512-O3y7XoWwE4zRvI5e1yVPDHyDGtEgGEI10KxTbQeMbEEt+imR7uxbL5Z4BCaHz5M09d1pkrFaqeYc8beAce4VSw== +formdata-node@^4.0.0: + version "4.3.0" + resolved "https://registry.npmjs.org/formdata-node/-/formdata-node-4.3.0.tgz#77be2add9092cbd1e1f4d35bc3293a89be117a04" + integrity sha512-TwqhWUZd2jB5l0kUhhcy1XYNsXq46NH6k60zmiu7xsxMztul+cCMuPSAQrSDV62zznhBKJdA9O+zeWj5i5Pbfg== dependencies: - fetch-blob "2.1.2" - form-data-encoder "1.4.3" node-domexception "1.0.0" + web-streams-polyfill "4.0.0-beta.1" formidable@^1.2.0, formidable@^1.2.2: version "1.2.2" @@ -15508,10 +15483,10 @@ highlight.js@^10.1.0, highlight.js@^10.1.1, highlight.js@^10.4.1, highlight.js@^ resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.2.tgz#89319b861edc66c48854ed1e6da21ea89f847360" integrity sha512-oFLl873u4usRM9K63j4ME9u3etNF0PLiJhSQ8rdfuL51Wn3zkD6drf9ZW0dOzjnZI22YYG24z30JcmfCZjMgYg== -highlight.js@~10.4.0: - version "10.4.1" - resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.4.1.tgz#d48fbcf4a9971c4361b3f95f302747afe19dbad0" - integrity sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg== +highlight.js@~10.7.0: + version "10.7.3" + resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" + integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== history@^5.0.0: version "5.0.0" @@ -15968,7 +15943,7 @@ immer@^9.0.1: resolved "https://registry.npmjs.org/immer/-/immer-9.0.6.tgz#7a96bf2674d06c8143e327cbf73539388ddf1a73" integrity sha512-G95ivKpy+EvVAnAab4fVa4YGYn24J1SpEktnJX7JJ45Bd7xqME/SCplFzYFmTbrkwZbQ4xJK1xMTUYBkN6pWsQ== -immutable@>=3.8.2, immutable@^3.8.1, immutable@^3.8.2, immutable@^3.x.x: +immutable@>=3.8.2, immutable@^3.8.2, immutable@^3.x.x: version "3.8.2" resolved "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" integrity sha1-wkOZUUVbs5kT2vKBN28VMOEErfM= @@ -16225,7 +16200,7 @@ into-stream@^5.1.1: from2 "^2.3.0" p-is-promise "^3.0.0" -invariant@^2.0.0, invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: +invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: version "2.2.4" resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -16448,7 +16423,7 @@ is-docker@^2.0.0, is-docker@^2.1.1: resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== -is-dom@^1.0.0, is-dom@^1.0.9: +is-dom@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/is-dom/-/is-dom-1.1.0.tgz#af1fced292742443bb59ca3f76ab5e80907b4e8a" integrity sha512-u82f6mvhYxRPKpw8V1N0W8ce1xXwOrQtgGcxl6UCL5zBmZu3is/18K0rR7uFCnMDuAsS/3W54mGL4vsaFUQlEQ== @@ -18585,11 +18560,6 @@ lodash-es@^4.17.15: resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== -lodash-es@^4.2.1: - version "4.17.15" - resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" - integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== - lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -18785,7 +18755,7 @@ lodash@4.17.15: resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@~4.17.0, lodash@~4.17.15, lodash@~4.17.4: +lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.0, lodash@~4.17.15, lodash@~4.17.4: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -18890,12 +18860,12 @@ lowercase-keys@^2.0.0: integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== lowlight@^1.14.0, lowlight@^1.17.0: - version "1.17.0" - resolved "https://registry.npmjs.org/lowlight/-/lowlight-1.17.0.tgz#a1143b2fba8239df8cd5893f9fe97aaf8465af4a" - integrity sha512-vmtBgYKD+QVNy7tIa7ulz5d//Il9R4MooOVh4nkOf9R9Cb/Dk5TXMSTieg/vDulkBkIWj59/BIlyFQxT9X1oAQ== + version "1.20.0" + resolved "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz#ddb197d33462ad0d93bf19d17b6c301aa3941888" + integrity sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw== dependencies: fault "^1.0.0" - highlight.js "~10.4.0" + highlight.js "~10.7.0" lru-cache@^4.0.1, lru-cache@^4.1.3: version "4.1.5" @@ -21530,11 +21500,6 @@ pend@~1.2.0: resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= -performance-now@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" - integrity sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU= - performance-now@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -22723,7 +22688,7 @@ raf-schd@^4.0.2: resolved "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.2.tgz#bd44c708188f2e84c810bf55fcea9231bcaed8a0" integrity sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ== -raf@^3.1.0, raf@^3.4.0, raf@^3.4.1: +raf@^3.4.0, raf@^3.4.1: version "3.4.1" resolved "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== @@ -22842,10 +22807,10 @@ react-copy-to-clipboard@5.0.3: copy-to-clipboard "^3" prop-types "^15.5.8" -react-debounce-input@^3.2.3: - version "3.2.3" - resolved "https://registry.npmjs.org/react-debounce-input/-/react-debounce-input-3.2.3.tgz#9e8c69771a621c81e8fe36b45ade49a95059cd87" - integrity sha512-7Bfjm9sxrtgB+IsSrdXoo4CVqKg7CbWC68dNhr8q7ZmY6C0AqtR524//SenHQWT+eeSG9DmSLWNWCUFSyaaWSQ== +react-debounce-input@=3.2.4: + version "3.2.4" + resolved "https://registry.npmjs.org/react-debounce-input/-/react-debounce-input-3.2.4.tgz#8204373a6498776536a2fcc7e467d054c3b729d4" + integrity sha512-fX70bNj0fLEYO2Zcvuh7eh9wOUQ29GIx6r8IxIJlc0i0mpUH++9ax0BhfAYfzndADli3RAMROrZQ014J01owrg== dependencies: lodash.debounce "^4" prop-types "^15.7.2" @@ -23001,23 +22966,12 @@ react-immutable-proptypes@2.2.0: dependencies: invariant "^2.2.2" -react-immutable-pure-component@^1.1.1: - version "1.2.3" - resolved "https://registry.npmjs.org/react-immutable-pure-component/-/react-immutable-pure-component-1.2.3.tgz#fa33638df68cfe9f73ccbee1d5861c17f3053f86" - integrity sha512-kNy2A/fDrSuR8TKwB+4ynmItmp1vgF87tWxxfmadwDYo2J3ANipHqTjDIBvJvJ7libvuh76jIbvmK0krjtKH1g== - optionalDependencies: - "@types/react" "16.4.6" +react-immutable-pure-component@^2.2.0: + version "2.2.2" + resolved "https://registry.npmjs.org/react-immutable-pure-component/-/react-immutable-pure-component-2.2.2.tgz#3014d3e20cd5a7a4db73b81f1f1464f4d351684b" + integrity sha512-vkgoMJUDqHZfXXnjVlG3keCxSO/U6WeDQ5/Sl0GK2cH8TOxEzQ5jXqDXHEL/jqk6fsNxV05oH5kD7VNMUE2k+A== -react-inspector@^2.3.0: - version "2.3.1" - resolved "https://registry.npmjs.org/react-inspector/-/react-inspector-2.3.1.tgz#f0eb7f520669b545b441af9d38ec6d706e5f649c" - integrity sha512-tUUK7t3KWgZEIUktOYko5Ic/oYwvjEvQUFAGC1UeMeDaQ5za2yZFtItJa2RTwBJB//NxPr000WQK6sEbqC6y0Q== - dependencies: - babel-runtime "^6.26.0" - is-dom "^1.0.9" - prop-types "^15.6.1" - -react-inspector@^5.1.0: +react-inspector@^5.1.0, react-inspector@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/react-inspector/-/react-inspector-5.1.1.tgz#58476c78fde05d5055646ed8ec02030af42953c8" integrity sha512-GURDaYzoLbW8pMGXwYPDBIv6nqei4kK7LPRZ9q9HCZF54wqXz/dnylBp/kfE9XmekBhHvLDdcYeyIwSrvtOiWg== @@ -23026,7 +22980,7 @@ react-inspector@^5.1.0: is-dom "^1.0.0" prop-types "^15.0.0" -react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: +react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -23072,15 +23026,6 @@ react-markdown@^5.0.2: unist-util-visit "^2.0.0" xtend "^4.0.1" -react-motion@^0.5.2: - version "0.5.2" - resolved "https://registry.npmjs.org/react-motion/-/react-motion-0.5.2.tgz#0dd3a69e411316567927917c6626551ba0607316" - integrity sha512-9q3YAvHoUiWlP3cK0v+w1N5Z23HXMj4IF4YuvjvWegWqNPfLXsOBE/V7UvQGpXxHFKRQQcNcVQE31g9SB/6qgQ== - dependencies: - performance-now "^0.2.0" - prop-types "^15.5.8" - raf "^3.1.0" - react-popper-tooltip@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-3.1.1.tgz#329569eb7b287008f04fcbddb6370452ad3f9eac" @@ -23098,28 +23043,17 @@ react-popper@^2.2.4: react-fast-compare "^3.0.1" warning "^4.0.2" -react-redux@=4.4.10: - version "4.4.10" - resolved "https://registry.npmjs.org/react-redux/-/react-redux-4.4.10.tgz#ad57bd1db00c2d0aa7db992b360ce63dd0b80ec5" - integrity sha512-tjL0Bmpkj75Td0k+lXlF8Fc8a9GuXFv/3ahUOCXExWs/jhsKiQeTffdH0j5byejCGCRL4tvGFYlrwBF1X/Aujg== +react-redux@^7.1.1, react-redux@^7.2.4: + version "7.2.5" + resolved "https://registry.npmjs.org/react-redux/-/react-redux-7.2.5.tgz#213c1b05aa1187d9c940ddfc0b29450957f6a3b8" + integrity sha512-Dt29bNyBsbQaysp6s/dN0gUodcq+dVKKER8Qv82UrpeygwYeX1raTtil7O/fftw/rFqzaf6gJhDZRkkZnn6bjg== dependencies: - create-react-class "^15.5.1" - hoist-non-react-statics "^3.3.0" - invariant "^2.0.0" - lodash "^4.17.11" + "@babel/runtime" "^7.12.1" + "@types/react-redux" "^7.1.16" + hoist-non-react-statics "^3.3.2" loose-envify "^1.4.0" prop-types "^15.7.2" - -react-redux@^7.1.1: - version "7.2.0" - resolved "https://registry.npmjs.org/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d" - integrity sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA== - dependencies: - "@babel/runtime" "^7.5.5" - hoist-non-react-statics "^3.3.0" - loose-envify "^1.4.0" - prop-types "^15.7.2" - react-is "^16.9.0" + react-is "^16.13.1" react-refresh@^0.8.3: version "0.8.3" @@ -23201,7 +23135,7 @@ react-syntax-highlighter@^13.5.3: prismjs "^1.21.0" refractor "^3.1.0" -react-syntax-highlighter@^15.4.3: +react-syntax-highlighter@^15.4.3, react-syntax-highlighter@^15.4.4: version "15.4.4" resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.4.4.tgz#dc9043f19e7bd063ff3ea78986d22a6eaa943b2a" integrity sha512-PsOFHNTzkb3OroXdoR897eKN5EZ6grht1iM+f1lJSq7/L0YVnkJaNVwC3wEUYPOAmeyl5xyer1DjL6MrumO6Zw== @@ -23614,22 +23548,17 @@ reduce-function-call@^1.0.1: dependencies: balanced-match "^1.0.0" -redux-immutable@3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/redux-immutable/-/redux-immutable-3.1.0.tgz#cafbd686e0711261119b9c28960935dc47a49d0a" - integrity sha1-yvvWhuBxEmERm5wolgk13EeknQo= - dependencies: - immutable "^3.8.1" +redux-immutable@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/redux-immutable/-/redux-immutable-4.0.0.tgz#3a1a32df66366462b63691f0e1dc35e472bbc9f3" + integrity sha1-Ohoy32Y2ZGK2NpHw4dw15HK7yfM= -redux@=3.7.2: - version "3.7.2" - resolved "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b" - integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A== +redux@^4.0.0, redux@^4.1.0: + version "4.1.1" + resolved "https://registry.npmjs.org/redux/-/redux-4.1.1.tgz#76f1c439bb42043f985fbd9bf21990e60bd67f47" + integrity sha512-hZQZdDEM25UY2P493kPYuKqviVwZ58lEmGQNeQ+gXa+U0gYPUBf7NKYazbe3m+bs/DzM/ahN12DbF+NG8i0CWw== dependencies: - lodash "^4.2.1" - lodash-es "^4.2.1" - loose-envify "^1.1.0" - symbol-observable "^1.0.3" + "@babel/runtime" "^7.9.2" redux@^4.0.4: version "4.0.5" @@ -25435,13 +25364,6 @@ stream-transform@^2.0.1: dependencies: mixme "^0.3.1" -stream@^0.0.2: - version "0.0.2" - resolved "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz#7f5363f057f6592c5595f00bc80a27f5cec1f0ef" - integrity sha1-f1Nj8Ff2WSxVlfALyAon9c7B8O8= - dependencies: - emitter-component "^1.1.1" - streamsearch@0.1.2, streamsearch@~0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" @@ -25898,10 +25820,10 @@ svgo@^1.0.0, svgo@^1.2.2: unquote "~1.1.1" util.promisify "~1.0.0" -swagger-client@^3.15.0: - version "3.15.0" - resolved "https://registry.npmjs.org/swagger-client/-/swagger-client-3.15.0.tgz#ba9de5830a8baf40ec5a867e815fa4588d974fa3" - integrity sha512-8Ki4bVbT+bl8hGmy3vR89wktZhVLXKNUk8CoUvfwn7Rq45bZ15c9UQXVr4VU6hP3f9diWJGJs0bKZR9pPOq4ZA== +swagger-client@^3.16.1: + version "3.16.1" + resolved "https://registry.npmjs.org/swagger-client/-/swagger-client-3.16.1.tgz#df86c9d407ab52c00cb356e714b0ec732bb3ad40" + integrity sha512-BcNRQzXHRGuXfhN0f80ptlr+bSaPvXwo8+gWbpmTnbKdAjcWOKAWwUx7rgGHjTKZh0qROr/GX9xOZIY8LrBuTg== dependencies: "@babel/runtime-corejs3" "^7.11.2" btoa "^1.2.1" @@ -25910,24 +25832,22 @@ swagger-client@^3.15.0: cross-fetch "^3.1.4" deep-extend "~0.6.0" fast-json-patch "^3.0.0-1" - form-data-encoder "^1.0.1" - formdata-node "^3.6.2" + form-data-encoder "^1.4.3" + formdata-node "^4.0.0" js-yaml "^4.1.0" - lodash "^4.17.19" + lodash "^4.17.21" qs "^6.9.4" querystring-browser "^1.0.4" traverse "~0.6.6" url "~0.11.0" -swagger-ui-react@^3.37.2: - version "3.52.0" - resolved "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-3.52.0.tgz#edf85fed58d55677fc49807ff7d67e43e2d5bf44" - integrity sha512-JBw2Omif7i8pFIW8PHsoHZDXhn1LhiPmzxl6NRYGaoCxKDi+HQAQthvZ3piJbDQ8vS3EjtlVh7lkBn/RtrkPww== +swagger-ui-react@^4.0.0-rc.3: + version "4.0.0-rc.3" + resolved "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-4.0.0-rc.3.tgz#393f424daf55272dd36737be654d978daf870d3d" + integrity sha512-Mo3+NvwLbbPy+ZJZoQkc3UudevSM03SHTZqwZOI7EY4KyMgqeet3xElnAUGZC94GqBZTstU0rf/znkgdaNo9qg== dependencies: "@babel/runtime-corejs3" "^7.14.7" "@braintree/sanitize-url" "^5.0.2" - "@kyleshockey/object-assign-deep" "^0.4.2" - "@kyleshockey/xml" "^1.0.2" base64-js "^1.5.1" classnames "^2.3.1" css.escape "1.5.1" @@ -25942,21 +25862,21 @@ swagger-ui-react@^3.37.2: prop-types "^15.7.2" randombytes "^2.1.0" react-copy-to-clipboard "5.0.3" - react-debounce-input "^3.2.3" + react-debounce-input "=3.2.4" react-immutable-proptypes "2.2.0" - react-immutable-pure-component "^1.1.1" - react-inspector "^2.3.0" - react-motion "^0.5.2" - react-redux "=4.4.10" - react-syntax-highlighter "^15.4.3" - redux "=3.7.2" - redux-immutable "3.1.0" + react-immutable-pure-component "^2.2.0" + react-inspector "^5.1.1" + react-redux "^7.2.4" + react-syntax-highlighter "^15.4.4" + redux "^4.1.0" + redux-immutable "^4.0.0" remarkable "^2.0.1" reselect "^4.0.0" serialize-error "^8.1.0" sha.js "^2.4.11" - swagger-client "^3.15.0" - url-parse "^1.5.1" + swagger-client "^3.16.1" + url-parse "^1.5.3" + xml "=1.0.1" xml-but-prettier "^1.0.1" zenscroll "^4.0.2" @@ -25967,7 +25887,7 @@ swap-case@^2.0.2: dependencies: tslib "^2.0.3" -symbol-observable@1.2.0, symbol-observable@^1.0.3, symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0: +symbol-observable@1.2.0, symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== @@ -27257,7 +27177,7 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" -url-parse@^1.4.3, url-parse@^1.5.1: +url-parse@^1.4.3, url-parse@^1.5.3: version "1.5.3" resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862" integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ== @@ -27669,6 +27589,11 @@ web-namespaces@^1.0.0: resolved "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== +web-streams-polyfill@4.0.0-beta.1: + version "4.0.0-beta.1" + resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.1.tgz#3b19b9817374b7cee06d374ba7eeb3aeb80e8c95" + integrity sha512-3ux37gEX670UUphBF9AMCq8XM6iQ8Ac6A+DSRRjDoRBm1ufCkaCDdNVbaqq60PsEkdNlLKrGtv/YBP4EJXqNtQ== + webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" @@ -28235,6 +28160,11 @@ xml2js@^0.4.11, xml2js@^0.4.19, xml2js@^0.4.23: sax ">=0.6.0" xmlbuilder "~11.0.0" +xml@=1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" + integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= + xmlbuilder@^15.1.1: version "15.1.1" resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5"