diff --git a/.changeset/brave-zoos-fail.md b/.changeset/brave-zoos-fail.md new file mode 100644 index 0000000000..3d24215cac --- /dev/null +++ b/.changeset/brave-zoos-fail.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-jenkins': patch +--- + +Avoid loading data from Jenkins twice. Don't load data when navigating through the pages as all data from all pages is already loaded. diff --git a/.changeset/purple-radios-complain.md b/.changeset/purple-radios-complain.md new file mode 100644 index 0000000000..2fde60ddff --- /dev/null +++ b/.changeset/purple-radios-complain.md @@ -0,0 +1,11 @@ +--- +'@backstage/plugin-jenkins': patch +--- + +Improve loading speed of the CI/CD page. +Only request the necessary fields from Jenkins to keep the request size low. +In addition everything is loaded in a single request, instead of requesting +each job and build individually. As this (and also the previous behavior) can +lead to a big amount of data, this limits the amount of jobs to 50. +For each job, only the latest build is loaded. Loading the full build history +of a job can lead to excessive load on the Jenkins instance. diff --git a/.changeset/rare-cooks-tan.md b/.changeset/rare-cooks-tan.md new file mode 100644 index 0000000000..790ab3503e --- /dev/null +++ b/.changeset/rare-cooks-tan.md @@ -0,0 +1,5 @@ +--- +'@backstage/core': patch +--- + +Fix React warning of descendant paragraph tag diff --git a/.changeset/seven-tips-begin.md b/.changeset/seven-tips-begin.md new file mode 100644 index 0000000000..5609315144 --- /dev/null +++ b/.changeset/seven-tips-begin.md @@ -0,0 +1,13 @@ +--- +'@backstage/plugin-api-docs': minor +--- + +Stop exposing a custom router from the `api-docs` plugin. Instead, use the +widgets exported by the plugin to compose your custom entity pages. + +Instead of displaying the API definitions directly in the API tab of the +component, it now contains tables linking to the API entities. This also adds +new widgets to display relationships (bot provides & consumes relationships) +between components and APIs. + +See the changelog of `create-app` for a migration guide. diff --git a/.changeset/seven-tips-more.md b/.changeset/seven-tips-more.md new file mode 100644 index 0000000000..9a6c4855c3 --- /dev/null +++ b/.changeset/seven-tips-more.md @@ -0,0 +1,136 @@ +--- +'@backstage/create-app': patch +--- + +Adjust template to the latest changes in the `api-docs` plugin. + +## Template Changes + +While updating to the latest `api-docs` plugin, the following changes are +necessary for the `create-app` template in your +`app/src/components/catalog/EntityPage.tsx`. This adds: + +- A custom entity page for API entities +- Changes the API tab to include the new `ConsumedApisCard` and + `ProvidedApisCard` that link to the API entity. + +```diff + import { ++ ApiDefinitionCard, +- Router as ApiDocsRouter, ++ ConsumedApisCard, ++ ProvidedApisCard, ++ ConsumedApisCard, ++ ConsumingComponentsCard, ++ ProvidedApisCard, ++ ProvidingComponentsCard + } from '@backstage/plugin-api-docs'; + +... + ++const ComponentApisContent = ({ entity }: { entity: Entity }) => ( ++ ++ ++ ++ ++ ++ ++ ++ ++); + + const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( + + } + /> + } + /> + } ++ element={} + /> +... + +-export const EntityPage = () => { +- const { entity } = useEntity(); +- switch (entity?.spec?.type) { +- case 'service': +- return ; +- case 'website': +- return ; +- default: +- return ; +- } +-}; + ++export const ComponentEntityPage = ({ entity }: { entity: Entity }) => { ++ switch (entity?.spec?.type) { ++ case 'service': ++ return ; ++ case 'website': ++ return ; ++ default: ++ return ; ++ } ++}; ++ ++const ApiOverviewContent = ({ entity }: { entity: Entity }) => ( ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++); ++ ++const ApiDefinitionContent = ({ entity }: { entity: ApiEntity }) => ( ++ ++ ++ ++ ++ ++); ++ ++const ApiEntityPage = ({ entity }: { entity: Entity }) => ( ++ ++ } ++ /> ++ } ++ /> ++ ++); ++ ++export const EntityPage = () => { ++ const { entity } = useEntity(); ++ ++ switch (entity?.kind?.toLowerCase()) { ++ case 'component': ++ return ; ++ case 'api': ++ return ; ++ default: ++ return ; ++ } ++}; +``` diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index 3741ff0aeb..6174130cc8 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -68,9 +68,9 @@ Firekube freben Fredrik github -Github +GitHub gitlab -Gitlab +GitLab Grafana graphql graphviz diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 3f59b815b3..0dc40d2c40 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -62,7 +62,7 @@ jobs: - name: prepare nightly release run: yarn changeset version --snapshot nightly - # Publishes the nightly release to NPM, by using tag we make sure the release is + # Publishes the nightly release to npm, by using tag we make sure the release is # not flagged as the latest release, which means that people will not get this # version of the package unless requested explicitly - name: publish nightly release diff --git a/.github/workflows/techdocs-project-board.yml b/.github/workflows/techdocs-project-board.yml index a8d476f713..679abe6536 100644 --- a/.github/workflows/techdocs-project-board.yml +++ b/.github/workflows/techdocs-project-board.yml @@ -1,7 +1,7 @@ name: Automatically add new TechDocs Issues and PRs to the GitHub project board # Development of TechDocs in Backstage is managed by this Kanban board - https://github.com/orgs/backstage/projects/1 # New issues and PRs with TechDocs in their title or docs-like-code label will be added to the board. -# Caveat: New PRs created from forks will not be added since GitHub actions don't share credentials with forks. +# Caveat: New PRs created from forks will not be added since GitHub Actions don't share credentials with forks. on: issues: diff --git a/docs/FAQ.md b/docs/FAQ.md index 90f642e591..da10f9724c 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -124,7 +124,7 @@ service. The `@backstage/create-app` tool that is used to create your own Backstage app is similar to [`create-react-app`](https://github.com/facebook/create-react-app) in that it gives you a starting point. The code you get is meant to be evolved, and most of -the functionality you get out of the box is brought in via NPM dependencies. +the functionality you get out of the box is brought in via npm dependencies. Keeping your app up to date generally means keeping your dependencies up to date. The Backstage CLI provides a command to help you with that. Simply run `yarn backstage-cli versions:bump` at the root of your repo, and the latest @@ -152,6 +152,27 @@ plugins to share dependencies between each other when possible. This contributes to Backstage being fast, which is an important part of the user and developer experience. +### Why are there no published Docker images or helm charts for Backstage? + +As mentioned above, Backstage is not a packaged service that you can use out of +the box. In order to get started with Backstage you need to use the +`@backstage/create-app` package to create and customize your own Backstage app. + +In order to build a Docker image from your own app, you can use the +`yarn build-image` command which is included out of the box in the app template. +By default this image will bundle up both the frontend and the backend into a +single image that you can deploy using your favorite tooling. + +There are also some examples that can help you deploy Backstage to kubernetes in +the +[contrib](https://github.com/backstage/backstage/tree/master/contrib/kubernetes) +folder. + +It is possible that example images will be provided in the future, which can be +used to quickly try out a small subset of the functionality of Backstage, but +these would not be able to provide much more functionality on top of what you +can see on a demo site. + ### Do I have to write plugins in TypeScript? No, you can use JavaScript if you prefer. We want to keep the Backstage core diff --git a/docs/architecture-decisions/adr005-catalog-core-entities.md b/docs/architecture-decisions/adr005-catalog-core-entities.md index f91698c5ff..34d6449c6b 100644 --- a/docs/architecture-decisions/adr005-catalog-core-entities.md +++ b/docs/architecture-decisions/adr005-catalog-core-entities.md @@ -58,7 +58,7 @@ discover existing functionality in the ecosystem. APIs are implemented by components and make their boundaries explicit. They might be defined using an RPC IDL (e.g. in Protobuf, GraphQL or similar), a data schema (e.g. in Avro, TFRecord or similar), or as code interfaces (e.g. -framework APIs in Swift, Kotlin, Java, C++, Typescript etc). In any case, APIs +framework APIs in Swift, Kotlin, Java, C++, TypeScript etc). In any case, APIs exposed by components need to be in a known machine-readable format so we can build further tooling and analysis on top. diff --git a/docs/architecture-decisions/adr006-avoid-react-fc.md b/docs/architecture-decisions/adr006-avoid-react-fc.md index 96f594daf7..51dcf042da 100644 --- a/docs/architecture-decisions/adr006-avoid-react-fc.md +++ b/docs/architecture-decisions/adr006-avoid-react-fc.md @@ -6,7 +6,7 @@ description: Architecture Decision Record (ADR) log on Avoid React.FC and React. ## Context -Facebook has removed `React.FC` from their base template for a Typescript +Facebook has removed `React.FC` from their base template for a TypeScript project. The reason for this was that it was found to be an unnecessary feature with next to no benefits in combination with a few downsides. diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index eb7a6fdee0..72e6388273 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -407,7 +407,7 @@ The current set of well-known and common values for this field is: - `service` - a backend service, typically exposing an API - `website` - a website -- `library` - a software library, such as an NPM module or a Java library +- `library` - a software library, such as an npm module or a Java library ### `spec.lifecycle` [required] @@ -558,7 +558,7 @@ The current set of well-known and common values for this field is: - `service` - a backend service, typically exposing an API - `website` - a website -- `library` - a software library, such as an NPM module or a Java library +- `library` - a software library, such as an npm module or a Java library ### `spec.templater` [required] diff --git a/docs/features/techdocs/architecture.md b/docs/features/techdocs/architecture.md index a71a23cabb..87fe969d46 100644 --- a/docs/features/techdocs/architecture.md +++ b/docs/features/techdocs/architecture.md @@ -21,7 +21,7 @@ looking at. In response, it receives the static files (HTML, CSS, JSON, etc.) to render on the page in TechDocs/Backstage. The static files consist of HTML, CSS and Images generated by MkDocs. We remove -all the Javascript before adding them to Backstage for security reasons. And +all the JavaScript before adding them to Backstage for security reasons. And there are some additional techdocs metadata JSON files that TechDocs needs to render a site. @@ -59,7 +59,7 @@ Similar to how it is done in the Basic setup, the TechDocs Reader requests your configured storage solution for the necessary files and returns them to TechDocs Reader. -We will provide instructions, scripts and/or templates (e.g. GitHub actions) to +We will provide instructions, scripts and/or templates (e.g. GitHub Actions) to generate docs in your CI/CD system. [Track progress here.](https://github.com/backstage/backstage/issues/3400) You will be able to use `techdocs-cli` to generate docs and publish the generated docs diff --git a/docs/getting-started/configure-app-with-plugins.md b/docs/getting-started/configure-app-with-plugins.md index 042d8964ca..64b9e772d1 100644 --- a/docs/getting-started/configure-app-with-plugins.md +++ b/docs/getting-started/configure-app-with-plugins.md @@ -11,7 +11,7 @@ add an existing plugin to it. We are using the [CircleCI](https://github.com/backstage/backstage/blob/master/plugins/circleci/README.md) plugin in this example. -1. Add the plugin's NPM package to the repo: +1. Add the plugin's npm package to the repo: ```bash yarn add @backstage/plugin-circleci diff --git a/docs/getting-started/create-an-app.md b/docs/getting-started/create-an-app.md index 6aef4c48c1..52ea90db5b 100644 --- a/docs/getting-started/create-an-app.md +++ b/docs/getting-started/create-an-app.md @@ -87,7 +87,7 @@ You may encounter the following error message: Couldn't find any versions for "file-saver" that matches "eligrey-FileSaver.js-1.3.8.tar.gz-art-external" ``` -This is likely because you have a globally configured NPM proxy, which breaks +This is likely because you have a globally configured npm proxy, which breaks the installation of the `material-table` dependency. This is a known issue and being worked on in `material-table`, but for now you can work around it using the following: diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index 7fc80b625c..16c837d676 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -10,7 +10,7 @@ you're planning to do. Creating a standalone instance makes it simpler to customize the application for your needs whilst staying up to date with the project. You will also depend on -`@backstage` packages from NPM, making the project much smaller. This is the +`@backstage` packages from npm, making the project much smaller. This is the recommended approach if you want to kick the tyres of Backstage or setup your own instance. diff --git a/docs/plugins/publishing.md b/docs/plugins/publishing.md index 7eab61891e..efea02ee23 100644 --- a/docs/plugins/publishing.md +++ b/docs/plugins/publishing.md @@ -1,16 +1,16 @@ --- id: publishing title: Publishing -description: Documentation on Publishing NPM packages +description: Documentation on Publishing npm packages --- -## NPM +## npm -NPM packages are published through CI/CD in the +npm packages are published through CI/CD in the [.github/workflows/master.yml](https://github.com/backstage/backstage/blob/master/.github/workflows/master.yml) workflow. Every commit that is merged to master will be checked for new versions of all public packages, and any new versions will automatically be published to -NPM. +npm. ### Creating a new release diff --git a/docs/plugins/testing.md b/docs/plugins/testing.md index e60b5fa8b7..b564fd732c 100644 --- a/docs/plugins/testing.md +++ b/docs/plugins/testing.md @@ -234,7 +234,7 @@ This is especially true for edge cases! ## Non-React Classes -Testing a Javascript object which is _not_ a React component follows a lot of +Testing a JavaScript object which is _not_ a React component follows a lot of the same principles as testing objects in other languages. ### API Testing Principles @@ -243,7 +243,7 @@ Testing an API involves verifying four things: 1. Invalid inputs are caught before being sent to the server. 2. Valid inputs translate into a valid browser request. -3. Server response is translated into an expected Javascript object. +3. Server response is translated into an expected JavaScript object. 4. Server errors are handled gracefully. ### Mocking API Calls diff --git a/docs/support/project-structure.md b/docs/support/project-structure.md index c25e087e57..190259841c 100644 --- a/docs/support/project-structure.md +++ b/docs/support/project-structure.md @@ -161,7 +161,7 @@ are separated out into their own folder, see further down. - [`docgen/`](https://github.com/backstage/backstage/tree/master/packages/docgen) - Uses the - [Typescript Compiler API](https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API) + [TypeScript Compiler API](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API) to read out definitions and generate documentation for it. - [`e2e-test/`](https://github.com/backstage/backstage/tree/master/packages/e2e-test) - diff --git a/docs/tutorials/journey.md b/docs/tutorials/journey.md index 664d4b77d3..adefdfa73f 100644 --- a/docs/tutorials/journey.md +++ b/docs/tutorials/journey.md @@ -22,7 +22,7 @@ music and wants to have a theme tune for every service in Backstage. Sam built a Spotify plugin for Backstage that allows service owners to define a theme tune for their service. The theme tune plays whenever a user visits the -service page in Backstage. The plugin is published to NPM and available for any +service page in Backstage. The plugin is published to npm and available for any organization to easily install and add to their Backstage installation. # 1. A New Plugin diff --git a/microsite/blog/2020-03-16-announcing-backstage.md b/microsite/blog/2020-03-16-announcing-backstage.md index 5824b76a57..5094de7b37 100644 --- a/microsite/blog/2020-03-16-announcing-backstage.md +++ b/microsite/blog/2020-03-16-announcing-backstage.md @@ -1,6 +1,6 @@ --- title: Announcing Backstage -author: Stefan Ålund +author: Stefan Ålund, Spotify authorURL: http://twitter.com/stalund authorImageURL: https://pbs.twimg.com/profile_images/121166861/6919c047c0d0edaace78c3009b28e917-user-full-200-130.generated_400x400.jpg --- diff --git a/microsite/blog/2020-03-18-what-is-backstage.md b/microsite/blog/2020-03-18-what-is-backstage.md index f4f62e2cf7..2c2b81d64d 100644 --- a/microsite/blog/2020-03-18-what-is-backstage.md +++ b/microsite/blog/2020-03-18-what-is-backstage.md @@ -1,6 +1,6 @@ --- title: What the heck is Backstage anyway? -author: Stefan Ålund +author: Stefan Ålund, Spotify authorURL: http://twitter.com/stalund authorImageURL: https://pbs.twimg.com/profile_images/121166861/6919c047c0d0edaace78c3009b28e917-user-full-200-130.generated_400x400.jpg --- diff --git a/microsite/blog/2020-04-06-lighthouse-plugin.md b/microsite/blog/2020-04-06-lighthouse-plugin.md index b8fd68e783..dcdb4b78e5 100644 --- a/microsite/blog/2020-04-06-lighthouse-plugin.md +++ b/microsite/blog/2020-04-06-lighthouse-plugin.md @@ -1,6 +1,6 @@ --- title: Introducing Lighthouse for Backstage -author: Paul Marbach +author: Paul Marbach, Spotify authorURL: http://twitter.com/fastfrwrd authorImageURL: https://pbs.twimg.com/profile_images/1224058798958088192/JPxS8uzR_400x400.jpg --- diff --git a/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md b/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md index 85f40dd9ea..65102c919f 100644 --- a/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md +++ b/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md @@ -1,6 +1,6 @@ --- title: How to quickly set up Backstage -author: Marcus Eide +author: Marcus Eide, Spotify authorURL: https://github.com/marcuseide authorImageURL: https://secure.gravatar.com/avatar/20223f1e03673c7c1e6282fbebaf6942 --- diff --git a/microsite/blog/2020-05-14-tech-radar-plugin.md b/microsite/blog/2020-05-14-tech-radar-plugin.md index 80c1eb8b5a..b78cf9004f 100644 --- a/microsite/blog/2020-05-14-tech-radar-plugin.md +++ b/microsite/blog/2020-05-14-tech-radar-plugin.md @@ -1,6 +1,6 @@ --- title: Introducing Tech Radar for Backstage -author: Bilawal Hameed +author: Bilawal Hameed, Spotify authorURL: http://twitter.com/bilawalhameed authorImageURL: https://avatars0.githubusercontent.com/bih --- diff --git a/microsite/blog/2020-05-14-weaveworks-covid-19-app-uses-backstage-ui.md b/microsite/blog/2020-05-14-weaveworks-covid-19-app-uses-backstage-ui.md index 68e3f903c7..3e3d8a537a 100644 --- a/microsite/blog/2020-05-14-weaveworks-covid-19-app-uses-backstage-ui.md +++ b/microsite/blog/2020-05-14-weaveworks-covid-19-app-uses-backstage-ui.md @@ -1,6 +1,6 @@ --- title: Weaveworks’ COVID-19 app uses Backstage UI -author: Jeff Feng +author: Jeff Feng, Spotify authorURL: https://github.com/fengypants authorImageURL: https://avatars2.githubusercontent.com/u/46946747 --- diff --git a/microsite/blog/2020-05-22-phase-2-service-catalog.md b/microsite/blog/2020-05-22-phase-2-service-catalog.md index 103750dcc6..520a2a5f10 100644 --- a/microsite/blog/2020-05-22-phase-2-service-catalog.md +++ b/microsite/blog/2020-05-22-phase-2-service-catalog.md @@ -1,6 +1,6 @@ --- title: Starting Phase 2: The Service Catalog -author: Stefan Ålund +author: Stefan Ålund, Spotify authorURL: http://twitter.com/stalund authorImageURL: https://pbs.twimg.com/profile_images/121166861/6919c047c0d0edaace78c3009b28e917-user-full-200-130.generated_400x400.jpg --- diff --git a/microsite/blog/2020-06-22-backstage-service-catalog-alpha.md b/microsite/blog/2020-06-22-backstage-service-catalog-alpha.md index e50d7a6d47..4519f16d96 100644 --- a/microsite/blog/2020-06-22-backstage-service-catalog-alpha.md +++ b/microsite/blog/2020-06-22-backstage-service-catalog-alpha.md @@ -1,6 +1,6 @@ --- title: Backstage Service Catalog released in alpha -author: Stefan Ålund +author: Stefan Ålund, Spotify authorURL: http://twitter.com/stalund image: https://backstage.io/blog/assets/6/header.png --- diff --git a/microsite/blog/2020-07-01-how-to-enable-authentication-in-backstage-using-passport.md b/microsite/blog/2020-07-01-how-to-enable-authentication-in-backstage-using-passport.md index 9d41a21d05..ce778c9dc2 100644 --- a/microsite/blog/2020-07-01-how-to-enable-authentication-in-backstage-using-passport.md +++ b/microsite/blog/2020-07-01-how-to-enable-authentication-in-backstage-using-passport.md @@ -1,6 +1,6 @@ --- title: How to enable authentication in Backstage using Passport -author: Lee Mills +author: Lee Mills, Spotify authorURL: https://github.com/leemills83 authorImageURL: https://avatars1.githubusercontent.com/u/1236238?s=460&v=4 --- diff --git a/microsite/blog/2020-08-05-announcing-backstage-software-templates.md b/microsite/blog/2020-08-05-announcing-backstage-software-templates.md index bd6ae8eeeb..afda20e499 100644 --- a/microsite/blog/2020-08-05-announcing-backstage-software-templates.md +++ b/microsite/blog/2020-08-05-announcing-backstage-software-templates.md @@ -1,6 +1,6 @@ --- title: Announcing Backstage Software Templates -author: Stefan Ålund +author: Stefan Ålund, Spotify authorURL: https://twitter.com/stalund --- diff --git a/microsite/blog/2020-09-08-announcing-tech-docs.md b/microsite/blog/2020-09-08-announcing-tech-docs.md index ceab17d7be..f09c73fd83 100644 --- a/microsite/blog/2020-09-08-announcing-tech-docs.md +++ b/microsite/blog/2020-09-08-announcing-tech-docs.md @@ -1,6 +1,6 @@ --- title: Announcing TechDocs: Spotify’s docs-like-code plugin for Backstage -author: Gary Niemen +author: Gary Niemen, Spotify authorURL: https://github.com/garyniemen --- diff --git a/microsite/blog/2020-09-23-backstage-cncf-sandbox.md b/microsite/blog/2020-09-23-backstage-cncf-sandbox.md index 8fc459cb62..48a67e878c 100644 --- a/microsite/blog/2020-09-23-backstage-cncf-sandbox.md +++ b/microsite/blog/2020-09-23-backstage-cncf-sandbox.md @@ -1,6 +1,6 @@ --- title: Backstage has been accepted into the CNCF Sandbox -author: Stefan Ålund +author: Stefan Ålund, Spotify authorURL: https://twitter.com/stalund --- diff --git a/microsite/blog/2020-09-30-backstage-design-system.md b/microsite/blog/2020-09-30-backstage-design-system.md index a1d087f755..fc227ea5d3 100644 --- a/microsite/blog/2020-09-30-backstage-design-system.md +++ b/microsite/blog/2020-09-30-backstage-design-system.md @@ -1,6 +1,6 @@ --- title: How to design for Backstage (even if you’re not a designer) -author: Kat Zhou +author: Kat Zhou, Spotify authorURL: http://twitter.com/katherinemzhou --- diff --git a/microsite/blog/2020-09-30-plugin-marketplace.md b/microsite/blog/2020-09-30-plugin-marketplace.md index 10928112b8..f4e9b749e2 100644 --- a/microsite/blog/2020-09-30-plugin-marketplace.md +++ b/microsite/blog/2020-09-30-plugin-marketplace.md @@ -1,6 +1,6 @@ --- title: The Plugin Marketplace is open -author: Stefan Ålund +author: Stefan Ålund, Spotify authorURL: https://twitter.com/stalund --- diff --git a/microsite/blog/2020-10-22-cost-insights-plugin.md b/microsite/blog/2020-10-22-cost-insights-plugin.md index 15647f8418..c7ae9c492d 100644 --- a/microsite/blog/2020-10-22-cost-insights-plugin.md +++ b/microsite/blog/2020-10-22-cost-insights-plugin.md @@ -1,6 +1,6 @@ --- title: New Cost Insights plugin: The engineer’s solution to taming cloud costs -author: Janisa Anandamohan +author: Janisa Anandamohan, Spotify authorURL: https://twitter.com/janisa_a --- diff --git a/microsite/siteConfig.js b/microsite/siteConfig.js index 3e246172f0..39ba05bcad 100644 --- a/microsite/siteConfig.js +++ b/microsite/siteConfig.js @@ -8,9 +8,6 @@ // See https://docusaurus.io/docs/site-config for all the possible // site configuration options. -// List of projects/orgs using your project for the users page. -const users = []; - const siteConfig = { title: 'Backstage Service Catalog and Developer Platform', // Title for your website. tagline: 'An open platform for building developer portals', @@ -72,11 +69,6 @@ const siteConfig = { navGroupSubcategoryTitleColor: '#9e9e9e', }, - /* Colors for syntax highlighting */ - highlight: { - theme: 'dark', - }, - // This copyright info is used in /core/Footer.js and blog RSS/Atom feeds. copyright: `Copyright © ${new Date().getFullYear()} Backstage Project Authors. All rights reserved. The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our Trademark Usage page: https://www.linuxfoundation.org/trademark-usage`, diff --git a/mkdocs.yml b/mkdocs.yml index 70123b384d..109be46a58 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -71,7 +71,7 @@ nav: - Testing: - Overview: 'plugins/testing.md' - Publishing: - - Open source and NPM: 'plugins/publishing.md' + - Open source and npm: 'plugins/publishing.md' - Private/internal (non-open source): 'plugins/publish-private.md' - Configuration: - Overview: 'conf/index.md' diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 2378afbc5e..a4d097b1d4 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -22,7 +22,10 @@ import { import { EmptyState } from '@backstage/core'; import { ApiDefinitionCard, - Router as ApiDocsRouter, + ConsumedApisCard, + ConsumingComponentsCard, + ProvidedApisCard, + ProvidingComponentsCard, } from '@backstage/plugin-api-docs'; import { AboutCard, @@ -187,6 +190,17 @@ const ComponentOverviewContent = ({ entity }: { entity: Entity }) => ( ); +const ComponentApisContent = ({ entity }: { entity: Entity }) => ( + + + + + + + + +); + const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( ( } + element={} /> ( + + + + + + + + ); diff --git a/packages/backend/package.json b/packages/backend/package.json index d48d2220b2..9b0ae65eed 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -3,11 +3,20 @@ "version": "0.2.5", "main": "dist/index.cjs.js", "types": "src/index.ts", - "private": true, "license": "Apache-2.0", + "private": true, "engines": { "node": "12 || 14" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "packages/backend" + }, + "keywords": [ + "backstage" + ], "scripts": { "build": "backstage-cli backend:build", "build-image": "backstage-cli backend:build-image --build --tag example-backend", diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index a343cae716..97e2c47422 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -11,6 +11,15 @@ "module": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "packages/catalog-client" + }, + "keywords": [ + "backstage" + ], "scripts": { "build": "backstage-cli build", "lint": "backstage-cli lint", diff --git a/packages/catalog-model/examples-relative/all-apis.yaml b/packages/catalog-model/examples-relative/all-apis.yaml index 20752faf97..e23f1b3656 100644 --- a/packages/catalog-model/examples-relative/all-apis.yaml +++ b/packages/catalog-model/examples-relative/all-apis.yaml @@ -10,3 +10,5 @@ spec: - ./apis/spotify-api.yaml - ./apis/streetlights-api.yaml - ./apis/swapi-graphql.yaml + - ./apis/wayback-archive-api.yaml + - ./apis/wayback-search-api.yaml diff --git a/packages/catalog-model/examples-relative/all-components.yaml b/packages/catalog-model/examples-relative/all-components.yaml index f29a7378a0..5db5825b20 100644 --- a/packages/catalog-model/examples-relative/all-components.yaml +++ b/packages/catalog-model/examples-relative/all-components.yaml @@ -14,3 +14,5 @@ spec: - ./components/playback-lib-component.yaml - ./components/www-artist-component.yaml - ./components/shuffle-api-component.yaml + - ./components/wayback-archive-component.yaml + - ./components/wayback-search-component.yaml diff --git a/packages/catalog-model/examples-relative/apis/wayback-archive-api.yaml b/packages/catalog-model/examples-relative/apis/wayback-archive-api.yaml new file mode 100644 index 0000000000..82610eae24 --- /dev/null +++ b/packages/catalog-model/examples-relative/apis/wayback-archive-api.yaml @@ -0,0 +1,11 @@ +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: wayback-archive + description: Archive API for the wayback machine +spec: + type: openapi + lifecycle: production + owner: archive@example.com + definition: + $text: https://github.com/APIs-guru/openapi-directory/blob/master/APIs/archive.org/wayback/1.0.0/openapi.yaml diff --git a/packages/catalog-model/examples-relative/apis/wayback-search-api.yaml b/packages/catalog-model/examples-relative/apis/wayback-search-api.yaml new file mode 100644 index 0000000000..6eb5cae54b --- /dev/null +++ b/packages/catalog-model/examples-relative/apis/wayback-search-api.yaml @@ -0,0 +1,11 @@ +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: wayback-search + description: Search API for the wayback machine +spec: + type: openapi + lifecycle: production + owner: archive@example.com + definition: + $text: https://github.com/APIs-guru/openapi-directory/blob/master/APIs/archive.org/search/1.0.0/openapi.yaml diff --git a/packages/catalog-model/examples-relative/components/wayback-archive-component.yaml b/packages/catalog-model/examples-relative/components/wayback-archive-component.yaml new file mode 100644 index 0000000000..ea5ef10a17 --- /dev/null +++ b/packages/catalog-model/examples-relative/components/wayback-archive-component.yaml @@ -0,0 +1,11 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: wayback-archive + description: Archive of the wayback machine +spec: + type: service + lifecycle: production + owner: archive@example.com + providesApis: + - wayback-archive diff --git a/packages/catalog-model/examples-relative/components/wayback-search-component.yaml b/packages/catalog-model/examples-relative/components/wayback-search-component.yaml new file mode 100644 index 0000000000..def136ff2f --- /dev/null +++ b/packages/catalog-model/examples-relative/components/wayback-search-component.yaml @@ -0,0 +1,13 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: wayback-search + description: Search of the wayback machine +spec: + type: service + lifecycle: production + owner: archive@example.com + providesApis: + - wayback-search + consumesApis: + - wayback-archive diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 25406c8eef..1d06be8054 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -11,6 +11,15 @@ "module": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "packages/catalog-model" + }, + "keywords": [ + "backstage" + ], "scripts": { "build": "backstage-cli build", "lint": "backstage-cli lint", diff --git a/packages/cli/README.md b/packages/cli/README.md index 9eac5cdfe4..cd6ad8094e 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -4,7 +4,7 @@ This package provides a CLI for developing Backstage plugins and apps. ## Installation -Install the package via npm or yarn: +Install the package via npm or Yarn: ```sh $ npm install --save @backstage/cli diff --git a/packages/cli/package.json b/packages/cli/package.json index 0d585436bb..a4ddf2e519 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -44,7 +44,7 @@ "@sucrase/webpack-loader": "^2.0.0", "@svgr/plugin-jsx": "5.4.x", "@svgr/plugin-svgo": "5.4.x", - "@svgr/rollup": "5.4.x", + "@svgr/rollup": "5.5.x", "@svgr/webpack": "5.4.x", "@types/start-server-webpack-plugin": "^2.2.0", "@types/webpack-env": "^1.15.2", diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index f773f279fc..e090c454f9 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -87,9 +87,9 @@ export function registerCommands(program: CommanderStatic) { 'Create plugin with the backend dependencies as default', ) .description('Creates a new plugin in the current repository') - .option('--scope ', 'NPM scope') - .option('--npm-registry ', 'NPM registry URL') - .option('--no-private', 'Public NPM Package') + .option('--scope ', 'npm scope') + .option('--npm-registry ', 'npm registry URL') + .option('--no-private', 'Public npm package') .action( lazy(() => import('./create-plugin/createPlugin').then(m => m.default)), ); diff --git a/packages/cli/src/lib/packager/index.ts b/packages/cli/src/lib/packager/index.ts index e9388ad9bc..3976998998 100644 --- a/packages/cli/src/lib/packager/index.ts +++ b/packages/cli/src/lib/packager/index.ts @@ -90,7 +90,7 @@ type Options = { * will be suitable for packaging e.g. into a docker image. * * This creates a structure that is functionally similar to if the packages where - * installed from NPM, but uses yarn workspaces to link to them at runtime. + * installed from npm, but uses Yarn workspaces to link to them at runtime. */ export async function createDistWorkspace( packageNames: string[], diff --git a/packages/cli/src/lib/versioning/Lockfile.ts b/packages/cli/src/lib/versioning/Lockfile.ts index fd7c189b40..f567786b7c 100644 --- a/packages/cli/src/lib/versioning/Lockfile.ts +++ b/packages/cli/src/lib/versioning/Lockfile.ts @@ -37,7 +37,7 @@ type LockfileQueryEntry = { version: string; }; -/** Entries that have an invalid version range, for example an NPM tag */ +/** Entries that have an invalid version range, for example an npm tag */ type AnalyzeResultInvalidRange = { name: string; range: string; diff --git a/packages/core-api/CHANGELOG.md b/packages/core-api/CHANGELOG.md index ab333b998e..23f2ba1545 100644 --- a/packages/core-api/CHANGELOG.md +++ b/packages/core-api/CHANGELOG.md @@ -62,7 +62,7 @@ ![](https://user-images.githubusercontent.com/872486/93851658-1a76f200-fce3-11ea-990b-26ca1a327a15.png) -- b79017fd3: Updated the `GithubAuth.create` method to configure the default scope of the Github Auth Api. As a result the +- b79017fd3: Updated the `GithubAuth.create` method to configure the default scope of the GitHub Auth Api. As a result the default scope is configurable when overwriting the Core Api in the app. ``` diff --git a/packages/core-api/src/apis/implementations/auth/github/GithubAuth.ts b/packages/core-api/src/apis/implementations/auth/github/GithubAuth.ts index 532bff3480..812032a8a3 100644 --- a/packages/core-api/src/apis/implementations/auth/github/GithubAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/github/GithubAuth.ts @@ -45,7 +45,7 @@ export type GithubAuthResponse = { const DEFAULT_PROVIDER = { id: 'github', - title: 'Github', + title: 'GitHub', icon: GithubIcon, }; diff --git a/packages/core-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts b/packages/core-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts index 8669dff022..3f4bc814e3 100644 --- a/packages/core-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts @@ -21,7 +21,7 @@ import { OAuthApiCreateOptions } from '../types'; const DEFAULT_PROVIDER = { id: 'gitlab', - title: 'Gitlab', + title: 'GitLab', icon: GitlabIcon, }; diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index d29bc53452..cb403685e1 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -21,7 +21,7 @@ ### Patch Changes - 7b37d65fd: Adds the MarkdownContent component to render and display Markdown content with the default - [GFM](https://github.github.com/gfm/) (Github flavored Markdown) dialect. + [GFM](https://github.github.com/gfm/) (GitHub Flavored Markdown) dialect. ``` @@ -59,7 +59,7 @@ - 482b6313d: Fix dense in Structured Metadata Table - 1c60f716e: Added EmptyState component -- b79017fd3: Updated the `GithubAuth.create` method to configure the default scope of the Github Auth Api. As a result the +- b79017fd3: Updated the `GithubAuth.create` method to configure the default scope of the GitHub Auth Api. As a result the default scope is configurable when overwriting the Core Api in the app. ``` diff --git a/packages/core/README.md b/packages/core/README.md index 0d0063c9fd..6d8519d46f 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -4,7 +4,7 @@ This package provides the core API used by Backstage plugins and apps. ## Installation -Install the package via npm or yarn: +Install the package via npm or Yarn: ```sh $ npm install --save @backstage/core diff --git a/packages/core/src/components/EmptyState/MissingAnnotationEmptyState.tsx b/packages/core/src/components/EmptyState/MissingAnnotationEmptyState.tsx index 1c27ba27d3..377373a06d 100644 --- a/packages/core/src/components/EmptyState/MissingAnnotationEmptyState.tsx +++ b/packages/core/src/components/EmptyState/MissingAnnotationEmptyState.tsx @@ -20,8 +20,7 @@ import { BackstageTheme } from '@backstage/theme'; import { EmptyState } from './EmptyState'; import { CodeSnippet } from '../CodeSnippet'; -const COMPONENT_YAML = `# Example -apiVersion: backstage.io/v1alpha1 +const COMPONENT_YAML = `apiVersion: backstage.io/v1alpha1 kind: Component metadata: name: example @@ -31,8 +30,7 @@ metadata: spec: type: website lifecycle: production - owner: guest -`; + owner: guest`; type Props = { annotation: string; @@ -49,10 +47,10 @@ const useStyles = makeStyles(theme => ({ export const MissingAnnotationEmptyState = ({ annotation }: Props) => { const classes = useStyles(); const description = ( - + <> The {annotation} annotation is missing. You need to add the annotation to your component if you want to enable this tool. - + ); return ( { text={COMPONENT_YAML.replace('ANNOTATION', annotation)} language="yaml" showLineNumbers - highlightedNumbers={[7, 8]} + highlightedNumbers={[6, 7]} customStyle={{ background: 'inherit', fontSize: '115%' }} /> diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index e13e31f406..66050cd50a 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -212,7 +212,7 @@ --config ../../app-config.yaml --config ../../app-config.development.yaml ``` -- 5a920c6e4: Updated naming of environment variables. New pattern [NAME]\_TOKEN for Github, Gitlab, Azure & Github enterprise access tokens. +- 5a920c6e4: Updated naming of environment variables. New pattern [NAME]\_TOKEN for GitHub, GitLab, Azure & GitHub Enterprise access tokens. ### Detail: diff --git a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx index 7d65b3261a..b5e384f7a9 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx @@ -13,26 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - Router as GitHubActionsRouter, - isPluginApplicableToEntity as isGitHubActionsAvailable, -} from '@backstage/plugin-github-actions'; -import { - Router as CircleCIRouter, - isPluginApplicableToEntity as isCircleCIAvailable, -} from '@backstage/plugin-circleci'; -import { Router as ApiDocsRouter } from '@backstage/plugin-api-docs'; -import { EmbeddedDocsRouter as DocsRouter } from '@backstage/plugin-techdocs'; - -import React from 'react'; -import { - EntityPageLayout, - useEntity, - AboutCard, -} from '@backstage/plugin-catalog'; -import { Entity } from '@backstage/catalog-model'; -import { Grid } from '@material-ui/core'; +import { ApiEntity, Entity } from '@backstage/catalog-model'; import { WarningPanel } from '@backstage/core'; +import { + ApiDefinitionCard, + ConsumedApisCard, + ConsumingComponentsCard, + ProvidedApisCard, + ProvidingComponentsCard +} from '@backstage/plugin-api-docs'; +import { + AboutCard, EntityPageLayout, + useEntity +} from '@backstage/plugin-catalog'; +import { + isPluginApplicableToEntity as isCircleCIAvailable, Router as CircleCIRouter +} from '@backstage/plugin-circleci'; +import { + isPluginApplicableToEntity as isGitHubActionsAvailable, Router as GitHubActionsRouter +} from '@backstage/plugin-github-actions'; +import { EmbeddedDocsRouter as DocsRouter } from '@backstage/plugin-techdocs'; +import { Grid } from '@material-ui/core'; +import React from 'react'; + const CICDSwitcher = ({ entity }: { entity: Entity }) => { // This component is just an example of how you can implement your company's logic in entity page. @@ -60,6 +63,17 @@ const OverviewContent = ({ entity }: { entity: Entity }) => ( ); +const ComponentApisContent = ({ entity }: { entity: Entity }) => ( + + + + + + + + +); + const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( ( } + element={} /> ( ); -export const EntityPage = () => { - const { entity } = useEntity(); +export const ComponentEntityPage = ({ entity }: { entity: Entity }) => { switch (entity?.spec?.type) { case 'service': return ; @@ -131,3 +144,55 @@ export const EntityPage = () => { return ; } }; + +const ApiOverviewContent = ({ entity }: { entity: Entity }) => ( + + + + + + + + + + + + + +); + +const ApiDefinitionContent = ({ entity }: { entity: ApiEntity }) => ( + + + + + +); + +const ApiEntityPage = ({ entity }: { entity: Entity }) => ( + + } + /> + } + /> + +); + +export const EntityPage = () => { + const { entity } = useEntity(); + + switch (entity?.kind?.toLowerCase()) { + case 'component': + return ; + case 'api': + return ; + default: + return ; + } +}; diff --git a/packages/dev-utils/README.md b/packages/dev-utils/README.md index 1aa86ba51c..4b08a5ca6d 100644 --- a/packages/dev-utils/README.md +++ b/packages/dev-utils/README.md @@ -6,7 +6,7 @@ This package provides utilities that help in developing plugins for Backstage, l ## Installation -Install the package via npm or yarn: +Install the package via npm or Yarn: ```sh $ npm install --save-dev @backstage/dev-utils diff --git a/packages/docgen/src/docgen/GitHubMarkdownPrinter.ts b/packages/docgen/src/docgen/GitHubMarkdownPrinter.ts index 08e493600d..d7955e5566 100644 --- a/packages/docgen/src/docgen/GitHubMarkdownPrinter.ts +++ b/packages/docgen/src/docgen/GitHubMarkdownPrinter.ts @@ -27,7 +27,7 @@ const COMMIT_SHA = execSync('git rev-parse HEAD').toString('utf8').trim(); /** - * The GithubMarkdownPrinter is a MarkdownPrinter for printing Github-flavored markdown documents. + * The GithubMarkdownPrinter is a MarkdownPrinter for printing GitHub Flavored Markdown documents. */ export default class GithubMarkdownPrinter implements MarkdownPrinter { private str: string = ''; diff --git a/packages/docgen/src/docgen/types.ts b/packages/docgen/src/docgen/types.ts index 7aff54d04c..1d1d93a6ae 100644 --- a/packages/docgen/src/docgen/types.ts +++ b/packages/docgen/src/docgen/types.ts @@ -31,7 +31,7 @@ export type TypeLink = { }; /** - * TypeInfo describes a Typescript Type. + * TypeInfo describes a TypeScript Type. */ export type TypeInfo = { id: number; diff --git a/packages/integration/package.json b/packages/integration/package.json index 8af3cb60b8..6ea0e5e2b0 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -11,6 +11,15 @@ "module": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "packages/integration" + }, + "keywords": [ + "backstage" + ], "scripts": { "build": "backstage-cli build", "lint": "backstage-cli lint", diff --git a/packages/integration/src/gitlab/core.ts b/packages/integration/src/gitlab/core.ts index 63dc3fdb3b..29dcc60bac 100644 --- a/packages/integration/src/gitlab/core.ts +++ b/packages/integration/src/gitlab/core.ts @@ -130,7 +130,7 @@ export async function getProjectId( const url = new URL(target); if (!url.pathname.includes('/-/blob/')) { - throw new Error('Please provide full path to yaml file from Gitlab'); + throw new Error('Please provide full path to yaml file from GitLab'); } try { diff --git a/packages/test-utils/README.md b/packages/test-utils/README.md index 38bf1e83cf..0f95d4ab26 100644 --- a/packages/test-utils/README.md +++ b/packages/test-utils/README.md @@ -4,7 +4,7 @@ This package provides utilities that can be used to test plugins and apps for Ba ## Installation -Install the package via npm or yarn: +Install the package via npm or Yarn: ```sh $ npm install --save-dev @backstage/test-utils diff --git a/packages/theme/README.md b/packages/theme/README.md index 4b29738193..9855d6730d 100644 --- a/packages/theme/README.md +++ b/packages/theme/README.md @@ -4,7 +4,7 @@ This package provides the extended Material UI Theme(s) that power Backstage. ## Installation -Install the package via npm or yarn: +Install the package via npm or Yarn: ```sh $ npm install --save @backstage/theme diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 659ded8061..e6deb39b48 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -9,6 +9,15 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/api-docs" + }, + "keywords": [ + "backstage" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/api-docs/src/catalog/EntityPageApi/EntityPageApi.tsx b/plugins/api-docs/src/catalog/EntityPageApi/EntityPageApi.tsx deleted file mode 100644 index e2148184b7..0000000000 --- a/plugins/api-docs/src/catalog/EntityPageApi/EntityPageApi.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { ComponentEntity, Entity } from '@backstage/catalog-model'; -import { Progress } from '@backstage/core'; -import { Grid } from '@material-ui/core'; -import React from 'react'; -import { - ApiDefinitionCard, - useComponentApiEntities, - useComponentApiNames, -} from '../../components'; - -type Props = { - entity: Entity; -}; - -export const EntityPageApi = ({ entity }: Props) => { - const apiNames = useComponentApiNames(entity as ComponentEntity); - - const { apiEntities, loading } = useComponentApiEntities({ - entity: entity as ComponentEntity, - }); - - if (loading) { - return ; - } - - return ( - - {apiNames.map(api => ( - - - - ))} - - ); -}; diff --git a/plugins/api-docs/src/catalog/Router.tsx b/plugins/api-docs/src/catalog/Router.tsx deleted file mode 100644 index 64c074fc46..0000000000 --- a/plugins/api-docs/src/catalog/Router.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react'; -import { Entity, RELATION_PROVIDES_API } from '@backstage/catalog-model'; -import { Route, Routes } from 'react-router'; -import { catalogRoute } from '../routes'; -import { EntityPageApi } from './EntityPageApi'; -import { MissingImplementsApisEmptyState } from './MissingImplementsApisEmptyState'; - -const isPluginApplicableToEntity = (entity: Entity) => { - // TODO: Also support RELATION_CONSUMES_API - return entity.relations?.some(r => r.type === RELATION_PROVIDES_API); -}; - -export const Router = ({ entity }: { entity: Entity }) => - !isPluginApplicableToEntity(entity) ? ( - - ) : ( - - } - /> - ) - - ); diff --git a/plugins/api-docs/src/components/ApisCards/ApisTable.tsx b/plugins/api-docs/src/components/ApisCards/ApisTable.tsx new file mode 100644 index 0000000000..7db62433eb --- /dev/null +++ b/plugins/api-docs/src/components/ApisCards/ApisTable.tsx @@ -0,0 +1,84 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ApiEntity } from '@backstage/catalog-model'; +import { Table, TableColumn } from '@backstage/core'; +import React from 'react'; +import { ApiTypeTitle } from '../ApiDefinitionCard'; +import { EntityLink } from '../EntityLink'; + +const columns: TableColumn[] = [ + { + title: 'Name', + field: 'metadata.name', + highlight: true, + render: (entity: any) => ( + {entity.metadata.name} + ), + }, + { + title: 'Owner', + field: 'spec.owner', + }, + { + title: 'Lifecycle', + field: 'spec.lifecycle', + }, + { + title: 'Type', + field: 'spec.type', + render: (entity: ApiEntity) => , + }, + { + title: 'Description', + field: 'metadata.description', + width: 'auto', + }, +]; + +type Props = { + title: string; + variant?: string; + entities: (ApiEntity | undefined)[]; +}; + +export const ApisTable = ({ entities, title, variant = 'gridItem' }: Props) => { + const tableStyle: React.CSSProperties = { + minWidth: '0', + width: '100%', + }; + + if (variant === 'gridItem') { + tableStyle.height = 'calc(100% - 10px)'; + } + + return ( + + columns={columns} + title={title} + style={tableStyle} + options={{ + // TODO: Toolbar padding if off compared to other cards, should be: padding: 16px 24px; + search: false, + paging: false, + actionsColumnIndex: -1, + padding: 'dense', + }} + // TODO: For now we skip all APIs that we can't find without a warning! + data={entities.filter(e => e !== undefined) as ApiEntity[]} + /> + ); +}; diff --git a/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.test.tsx b/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.test.tsx new file mode 100644 index 0000000000..a4a1e15511 --- /dev/null +++ b/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.test.tsx @@ -0,0 +1,127 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity, RELATION_CONSUMES_API } from '@backstage/catalog-model'; +import { ApiProvider, ApiRegistry } from '@backstage/core'; +import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog'; +import { renderInTestApp } from '@backstage/test-utils'; +import { waitFor } from '@testing-library/react'; +import React from 'react'; +import { ApiDocsConfig, apiDocsConfigRef } from '../../config'; +import { ConsumedApisCard } from './ConsumedApisCard'; + +describe('', () => { + const apiDocsConfig: jest.Mocked = { + getApiDefinitionWidget: jest.fn(), + } as any; + const catalogApi: jest.Mocked = { + getLocationById: jest.fn(), + getEntityByName: jest.fn(), + getEntities: jest.fn(), + addLocation: jest.fn(), + getLocationByEntity: jest.fn(), + removeEntityByUid: jest.fn(), + } as any; + let Wrapper: React.ComponentType; + + beforeEach(() => { + const apis = ApiRegistry.with(catalogApiRef, catalogApi).with( + apiDocsConfigRef, + apiDocsConfig, + ); + + Wrapper = ({ children }: { children?: React.ReactNode }) => ( + {children} + ); + }); + + afterEach(() => jest.resetAllMocks()); + + it('shows empty list if no relations', async () => { + const entity: Entity = { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'my-name', + namespace: 'my-namespace', + }, + relations: [], + }; + + const { getByText } = await renderInTestApp( + + + , + ); + + expect(getByText(/Consumed APIs/i)).toBeInTheDocument(); + expect(getByText(/No APIs consumed by this entity/i)).toBeInTheDocument(); + }); + + it('shows consumed APIs', async () => { + const entity: Entity = { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'my-name', + namespace: 'my-namespace', + }, + relations: [ + { + target: { + kind: 'API', + namespace: 'my-namespace', + name: 'target-name', + }, + type: RELATION_CONSUMES_API, + }, + ], + }; + catalogApi.getEntityByName.mockResolvedValue({ + apiVersion: 'v1', + kind: 'API', + metadata: { + name: 'target-name', + namespace: 'my-namespace', + }, + spec: { + type: 'openapi', + owner: 'Test', + lifecycle: 'production', + definition: '...', + }, + }); + apiDocsConfig.getApiDefinitionWidget.mockReturnValue({ + type: 'openapi', + title: 'OpenAPI', + component: () =>
, + }); + + const { getByText } = await renderInTestApp( + + + , + ); + + await waitFor(() => { + expect(getByText(/Consumed APIs/i)).toBeInTheDocument(); + expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(getByText(/OpenAPI/)).toBeInTheDocument(); + expect(getByText(/Test/i)).toBeInTheDocument(); + expect(getByText(/production/i)).toBeInTheDocument(); + }); + }); +}); diff --git a/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx b/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx new file mode 100644 index 0000000000..0bd4919554 --- /dev/null +++ b/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx @@ -0,0 +1,85 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + ApiEntity, + Entity, + RELATION_CONSUMES_API, +} from '@backstage/catalog-model'; +import { EmptyState, InfoCard, Progress } from '@backstage/core'; +import React, { PropsWithChildren } from 'react'; +import { ApisTable } from './ApisTable'; +import { MissingConsumesApisEmptyState } from '../EmptyState'; +import { useRelatedEntities } from '../useRelatedEntities'; + +const ApisCard = ({ + children, + variant = 'gridItem', +}: PropsWithChildren<{ variant?: string }>) => { + return ( + + {children} + + ); +}; + +type Props = { + entity: Entity; + variant?: string; +}; + +export const ConsumedApisCard = ({ entity, variant = 'gridItem' }: Props) => { + const { entities, loading, error } = useRelatedEntities( + entity, + RELATION_CONSUMES_API, + ); + + if (loading) { + return ( + + + + ); + } + + if (error) { + return ( + + + + ); + } + + if (!entities || entities.length === 0) { + return ( + + + + ); + } + + return ( + + ); +}; diff --git a/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.test.tsx b/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.test.tsx new file mode 100644 index 0000000000..1f42ff9060 --- /dev/null +++ b/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.test.tsx @@ -0,0 +1,127 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity, RELATION_PROVIDES_API } from '@backstage/catalog-model'; +import { ApiProvider, ApiRegistry } from '@backstage/core'; +import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog'; +import { renderInTestApp } from '@backstage/test-utils'; +import { waitFor } from '@testing-library/react'; +import React from 'react'; +import { ApiDocsConfig, apiDocsConfigRef } from '../../config'; +import { ProvidedApisCard } from './ProvidedApisCard'; + +describe('', () => { + const apiDocsConfig: jest.Mocked = { + getApiDefinitionWidget: jest.fn(), + } as any; + const catalogApi: jest.Mocked = { + getLocationById: jest.fn(), + getEntityByName: jest.fn(), + getEntities: jest.fn(), + addLocation: jest.fn(), + getLocationByEntity: jest.fn(), + removeEntityByUid: jest.fn(), + } as any; + let Wrapper: React.ComponentType; + + beforeEach(() => { + const apis = ApiRegistry.with(catalogApiRef, catalogApi).with( + apiDocsConfigRef, + apiDocsConfig, + ); + + Wrapper = ({ children }: { children?: React.ReactNode }) => ( + {children} + ); + }); + + afterEach(() => jest.resetAllMocks()); + + it('shows empty list if no relations', async () => { + const entity: Entity = { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'my-name', + namespace: 'my-namespace', + }, + relations: [], + }; + + const { getByText } = await renderInTestApp( + + + , + ); + + expect(getByText(/Provided APIs/i)).toBeInTheDocument(); + expect(getByText(/No APIs provided by this entity/i)).toBeInTheDocument(); + }); + + it('shows consumed APIs', async () => { + const entity: Entity = { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'my-name', + namespace: 'my-namespace', + }, + relations: [ + { + target: { + kind: 'API', + namespace: 'my-namespace', + name: 'target-name', + }, + type: RELATION_PROVIDES_API, + }, + ], + }; + catalogApi.getEntityByName.mockResolvedValue({ + apiVersion: 'v1', + kind: 'API', + metadata: { + name: 'target-name', + namespace: 'my-namespace', + }, + spec: { + type: 'openapi', + owner: 'Test', + lifecycle: 'production', + definition: '...', + }, + }); + apiDocsConfig.getApiDefinitionWidget.mockReturnValue({ + type: 'openapi', + title: 'OpenAPI', + component: () =>
, + }); + + const { getByText } = await renderInTestApp( + + + , + ); + + await waitFor(() => { + expect(getByText(/Provided APIs/i)).toBeInTheDocument(); + expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(getByText(/OpenAPI/)).toBeInTheDocument(); + expect(getByText(/Test/i)).toBeInTheDocument(); + expect(getByText(/production/i)).toBeInTheDocument(); + }); + }); +}); diff --git a/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx b/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx new file mode 100644 index 0000000000..618f2dc1f6 --- /dev/null +++ b/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx @@ -0,0 +1,85 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + ApiEntity, + Entity, + RELATION_PROVIDES_API, +} from '@backstage/catalog-model'; +import { EmptyState, InfoCard, Progress } from '@backstage/core'; +import React, { PropsWithChildren } from 'react'; +import { ApisTable } from './ApisTable'; +import { MissingProvidesApisEmptyState } from '../EmptyState'; +import { useRelatedEntities } from '../useRelatedEntities'; + +const ApisCard = ({ + children, + variant = 'gridItem', +}: PropsWithChildren<{ variant?: string }>) => { + return ( + + {children} + + ); +}; + +type Props = { + entity: Entity; + variant?: string; +}; + +export const ProvidedApisCard = ({ entity, variant = 'gridItem' }: Props) => { + const { entities, loading, error } = useRelatedEntities( + entity, + RELATION_PROVIDES_API, + ); + + if (loading) { + return ( + + + + ); + } + + if (error) { + return ( + + + + ); + } + + if (!entities || entities.length === 0) { + return ( + + + + ); + } + + return ( + + ); +}; diff --git a/plugins/api-docs/src/catalog/EntityPageApi/index.ts b/plugins/api-docs/src/components/ApisCards/index.ts similarity index 84% rename from plugins/api-docs/src/catalog/EntityPageApi/index.ts rename to plugins/api-docs/src/components/ApisCards/index.ts index 1d382e01de..2a01a1dc6e 100644 --- a/plugins/api-docs/src/catalog/EntityPageApi/index.ts +++ b/plugins/api-docs/src/components/ApisCards/index.ts @@ -14,4 +14,5 @@ * limitations under the License. */ -export { EntityPageApi } from './EntityPageApi'; +export { ConsumedApisCard } from './ConsumedApisCard'; +export { ProvidedApisCard } from './ProvidedApisCard'; diff --git a/plugins/api-docs/src/components/ComponentsCards/ComponentsTable.tsx b/plugins/api-docs/src/components/ComponentsCards/ComponentsTable.tsx new file mode 100644 index 0000000000..1b62a56d10 --- /dev/null +++ b/plugins/api-docs/src/components/ComponentsCards/ComponentsTable.tsx @@ -0,0 +1,87 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentEntity } from '@backstage/catalog-model'; +import { Table, TableColumn } from '@backstage/core'; +import React from 'react'; +import { EntityLink } from '../EntityLink'; + +const columns: TableColumn[] = [ + { + title: 'Name', + field: 'metadata.name', + highlight: true, + render: (entity: any) => ( + {entity.metadata.name} + ), + }, + { + title: 'Owner', + field: 'spec.owner', + }, + { + title: 'Lifecycle', + field: 'spec.lifecycle', + }, + { + title: 'Type', + field: 'spec.type', + }, + { + title: 'Description', + field: 'metadata.description', + width: 'auto', + }, +]; + +type Props = { + title: string; + variant?: string; + entities: (ComponentEntity | undefined)[]; +}; + +// TODO: In theory this could also be systems! +export const ComponentsTable = ({ + entities, + title, + variant = 'gridItem', +}: Props) => { + const tableStyle: React.CSSProperties = { + minWidth: '0', + width: '100%', + }; + + if (variant === 'gridItem') { + tableStyle.height = 'calc(100% - 10px)'; + } + + return ( + + columns={columns} + title={title} + style={tableStyle} + options={{ + // TODO: Toolbar padding if off compared to other cards, should be: padding: 16px 24px; + search: false, + paging: false, + actionsColumnIndex: -1, + padding: 'dense', + }} + // TODO: For now we skip all APIs that we can't find without a warning! + data={entities.filter(e => e !== undefined) as ComponentEntity[]} + /> + ); +}; diff --git a/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.test.tsx b/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.test.tsx new file mode 100644 index 0000000000..606ff7e77b --- /dev/null +++ b/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.test.tsx @@ -0,0 +1,125 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity, RELATION_API_CONSUMED_BY } from '@backstage/catalog-model'; +import { ApiProvider, ApiRegistry } from '@backstage/core'; +import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog'; +import { renderInTestApp } from '@backstage/test-utils'; +import { waitFor } from '@testing-library/react'; +import React from 'react'; +import { ConsumingComponentsCard } from './ConsumingComponentsCard'; + +describe('', () => { + const catalogApi: jest.Mocked = { + getLocationById: jest.fn(), + getEntityByName: jest.fn(), + getEntities: jest.fn(), + addLocation: jest.fn(), + getLocationByEntity: jest.fn(), + removeEntityByUid: jest.fn(), + } as any; + let Wrapper: React.ComponentType; + + beforeEach(() => { + const apis = ApiRegistry.with(catalogApiRef, catalogApi); + + Wrapper = ({ children }: { children?: React.ReactNode }) => ( + {children} + ); + }); + + afterEach(() => jest.resetAllMocks()); + + it('shows empty list if no relations', async () => { + const entity: Entity = { + apiVersion: 'v1', + kind: 'API', + metadata: { + name: 'my-name', + namespace: 'my-namespace', + }, + spec: { + type: 'openapi', + owner: 'Test', + lifecycle: 'production', + definition: '...', + }, + relations: [], + }; + + const { getByText } = await renderInTestApp( + + + , + ); + + expect(getByText(/Consumers/i)).toBeInTheDocument(); + expect(getByText(/No APIs consumed by this entity/i)).toBeInTheDocument(); + }); + + it('shows consuming components', async () => { + const entity: Entity = { + apiVersion: 'v1', + kind: 'API', + metadata: { + name: 'my-name', + namespace: 'my-namespace', + }, + spec: { + type: 'openapi', + owner: 'Test', + lifecycle: 'production', + definition: '...', + }, + relations: [ + { + target: { + kind: 'Component', + namespace: 'my-namespace', + name: 'target-name', + }, + type: RELATION_API_CONSUMED_BY, + }, + ], + }; + catalogApi.getEntityByName.mockResolvedValue({ + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'target-name', + namespace: 'my-namespace', + }, + spec: { + type: 'service', + owner: 'Test', + lifecycle: 'production', + }, + }); + + const { getByText } = await renderInTestApp( + + + , + ); + + await waitFor(() => { + expect(getByText(/Consumers/i)).toBeInTheDocument(); + expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(getByText(/Test/i)).toBeInTheDocument(); + expect(getByText(/production/i)).toBeInTheDocument(); + }); + }); +}); diff --git a/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.tsx b/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.tsx new file mode 100644 index 0000000000..0431367aa2 --- /dev/null +++ b/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.tsx @@ -0,0 +1,88 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + ComponentEntity, + Entity, + RELATION_API_CONSUMED_BY, +} from '@backstage/catalog-model'; +import { EmptyState, InfoCard, Progress } from '@backstage/core'; +import React, { PropsWithChildren } from 'react'; +import { MissingConsumesApisEmptyState } from '../EmptyState'; +import { useRelatedEntities } from '../useRelatedEntities'; +import { ComponentsTable } from './ComponentsTable'; + +const ComponentsCard = ({ + children, + variant = 'gridItem', +}: PropsWithChildren<{ variant?: string }>) => { + return ( + + {children} + + ); +}; + +type Props = { + entity: Entity; + variant?: string; +}; + +export const ConsumingComponentsCard = ({ + entity, + variant = 'gridItem', +}: Props) => { + const { entities, loading, error } = useRelatedEntities( + entity, + RELATION_API_CONSUMED_BY, + ); + + if (loading) { + return ( + + + + ); + } + + if (error) { + return ( + + + + ); + } + + if (!entities || entities.length === 0) { + return ( + + + + ); + } + + return ( + + ); +}; diff --git a/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.test.tsx b/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.test.tsx new file mode 100644 index 0000000000..d1cec1722a --- /dev/null +++ b/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.test.tsx @@ -0,0 +1,125 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity, RELATION_API_PROVIDED_BY } from '@backstage/catalog-model'; +import { ApiProvider, ApiRegistry } from '@backstage/core'; +import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog'; +import { renderInTestApp } from '@backstage/test-utils'; +import { waitFor } from '@testing-library/react'; +import React from 'react'; +import { ProvidingComponentsCard } from './ProvidingComponentsCard'; + +describe('', () => { + const catalogApi: jest.Mocked = { + getLocationById: jest.fn(), + getEntityByName: jest.fn(), + getEntities: jest.fn(), + addLocation: jest.fn(), + getLocationByEntity: jest.fn(), + removeEntityByUid: jest.fn(), + } as any; + let Wrapper: React.ComponentType; + + beforeEach(() => { + const apis = ApiRegistry.with(catalogApiRef, catalogApi); + + Wrapper = ({ children }: { children?: React.ReactNode }) => ( + {children} + ); + }); + + afterEach(() => jest.resetAllMocks()); + + it('shows empty list if no relations', async () => { + const entity: Entity = { + apiVersion: 'v1', + kind: 'API', + metadata: { + name: 'my-name', + namespace: 'my-namespace', + }, + spec: { + type: 'openapi', + owner: 'Test', + lifecycle: 'production', + definition: '...', + }, + relations: [], + }; + + const { getByText } = await renderInTestApp( + + + , + ); + + expect(getByText(/Providers/i)).toBeInTheDocument(); + expect(getByText(/No APIs provided by this entity/i)).toBeInTheDocument(); + }); + + it('shows providing components', async () => { + const entity: Entity = { + apiVersion: 'v1', + kind: 'API', + metadata: { + name: 'my-name', + namespace: 'my-namespace', + }, + spec: { + type: 'openapi', + owner: 'Test', + lifecycle: 'production', + definition: '...', + }, + relations: [ + { + target: { + kind: 'Component', + namespace: 'my-namespace', + name: 'target-name', + }, + type: RELATION_API_PROVIDED_BY, + }, + ], + }; + catalogApi.getEntityByName.mockResolvedValue({ + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'target-name', + namespace: 'my-namespace', + }, + spec: { + type: 'service', + owner: 'Test', + lifecycle: 'production', + }, + }); + + const { getByText } = await renderInTestApp( + + + , + ); + + await waitFor(() => { + expect(getByText(/Providers/i)).toBeInTheDocument(); + expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(getByText(/Test/i)).toBeInTheDocument(); + expect(getByText(/production/i)).toBeInTheDocument(); + }); + }); +}); diff --git a/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.tsx b/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.tsx new file mode 100644 index 0000000000..9e405a3af3 --- /dev/null +++ b/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.tsx @@ -0,0 +1,88 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + ComponentEntity, + Entity, + RELATION_API_PROVIDED_BY, +} from '@backstage/catalog-model'; +import { EmptyState, InfoCard, Progress } from '@backstage/core'; +import React, { PropsWithChildren } from 'react'; +import { MissingProvidesApisEmptyState } from '../EmptyState'; +import { useRelatedEntities } from '../useRelatedEntities'; +import { ComponentsTable } from './ComponentsTable'; + +const ComponentsCard = ({ + children, + variant = 'gridItem', +}: PropsWithChildren<{ variant?: string }>) => { + return ( + + {children} + + ); +}; + +type Props = { + entity: Entity; + variant?: string; +}; + +export const ProvidingComponentsCard = ({ + entity, + variant = 'gridItem', +}: Props) => { + const { entities, loading, error } = useRelatedEntities( + entity, + RELATION_API_PROVIDED_BY, + ); + + if (loading) { + return ( + + + + ); + } + + if (error) { + return ( + + + + ); + } + + if (!entities || entities.length === 0) { + return ( + + + + ); + } + + return ( + + ); +}; diff --git a/plugins/api-docs/src/catalog/index.ts b/plugins/api-docs/src/components/ComponentsCards/index.ts similarity index 81% rename from plugins/api-docs/src/catalog/index.ts rename to plugins/api-docs/src/components/ComponentsCards/index.ts index 4c177df914..e1c0e87198 100644 --- a/plugins/api-docs/src/catalog/index.ts +++ b/plugins/api-docs/src/components/ComponentsCards/index.ts @@ -14,4 +14,5 @@ * limitations under the License. */ -export { Router } from './Router'; +export { ConsumingComponentsCard } from './ConsumingComponentsCard'; +export { ProvidingComponentsCard } from './ProvidingComponentsCard'; diff --git a/plugins/api-docs/src/components/useComponentApiNames.ts b/plugins/api-docs/src/components/EmptyState/MissingConsumesApisEmptyState.test.tsx similarity index 57% rename from plugins/api-docs/src/components/useComponentApiNames.ts rename to plugins/api-docs/src/components/EmptyState/MissingConsumesApisEmptyState.test.tsx index 1303967895..de753713c3 100644 --- a/plugins/api-docs/src/components/useComponentApiNames.ts +++ b/plugins/api-docs/src/components/EmptyState/MissingConsumesApisEmptyState.test.tsx @@ -14,16 +14,15 @@ * limitations under the License. */ -import { - ComponentEntity, - RELATION_PROVIDES_API, -} from '@backstage/catalog-model'; +import { renderInTestApp } from '@backstage/test-utils'; +import React from 'react'; +import { MissingConsumesApisEmptyState } from './MissingConsumesApisEmptyState'; -export const useComponentApiNames = (entity: ComponentEntity) => { - // TODO: This code doesn't handle namespaces and kinds correctly, but will be removed soon - return ( - entity.relations - ?.filter(r => r.type === RELATION_PROVIDES_API) - ?.map(r => r.target.name) || [] - ); -}; +describe('', () => { + it('renders without exploding', async () => { + const { getByText } = await renderInTestApp( + , + ); + expect(getByText(/consumesApis:/i)).toBeInTheDocument(); + }); +}); diff --git a/plugins/api-docs/src/components/EmptyState/MissingConsumesApisEmptyState.tsx b/plugins/api-docs/src/components/EmptyState/MissingConsumesApisEmptyState.tsx new file mode 100644 index 0000000000..3e71168dde --- /dev/null +++ b/plugins/api-docs/src/components/EmptyState/MissingConsumesApisEmptyState.tsx @@ -0,0 +1,81 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { Button, makeStyles, Typography } from '@material-ui/core'; +import { BackstageTheme } from '@backstage/theme'; +import { CodeSnippet, EmptyState } from '@backstage/core'; + +const COMPONENT_YAML = `# Example +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: example +spec: + type: service + lifecycle: production + owner: guest + consumesApis: + - example-api +`; + +const useStyles = makeStyles(theme => ({ + code: { + borderRadius: 6, + margin: `${theme.spacing(2)}px 0px`, + background: theme.palette.type === 'dark' ? '#444' : '#fff', + }, +})); + +export const MissingConsumesApisEmptyState = () => { + const classes = useStyles(); + return ( + + Components can consume APIs that are displayed on this page. You need + to fill the consumesApis field to enable this tool. + + } + action={ + <> + + Link an API to your component as shown in the highlighted example + below: + +
+ +
+ + + } + /> + ); +}; diff --git a/plugins/api-docs/src/components/EmptyState/MissingProvidesApisEmptyState.test.tsx b/plugins/api-docs/src/components/EmptyState/MissingProvidesApisEmptyState.test.tsx new file mode 100644 index 0000000000..b539753a95 --- /dev/null +++ b/plugins/api-docs/src/components/EmptyState/MissingProvidesApisEmptyState.test.tsx @@ -0,0 +1,28 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { renderInTestApp } from '@backstage/test-utils'; +import React from 'react'; +import { MissingProvidesApisEmptyState } from './MissingProvidesApisEmptyState'; + +describe('', () => { + it('renders without exploding', async () => { + const { getByText } = await renderInTestApp( + , + ); + expect(getByText(/providesApis:/i)).toBeInTheDocument(); + }); +}); diff --git a/plugins/api-docs/src/catalog/MissingImplementsApisEmptyState/MissingImplementsApisEmptyState.tsx b/plugins/api-docs/src/components/EmptyState/MissingProvidesApisEmptyState.tsx similarity index 95% rename from plugins/api-docs/src/catalog/MissingImplementsApisEmptyState/MissingImplementsApisEmptyState.tsx rename to plugins/api-docs/src/components/EmptyState/MissingProvidesApisEmptyState.tsx index fbb8810088..9bf3465a34 100644 --- a/plugins/api-docs/src/catalog/MissingImplementsApisEmptyState/MissingImplementsApisEmptyState.tsx +++ b/plugins/api-docs/src/components/EmptyState/MissingProvidesApisEmptyState.tsx @@ -40,12 +40,12 @@ const useStyles = makeStyles(theme => ({ }, })); -export const MissingImplementsApisEmptyState = () => { +export const MissingProvidesApisEmptyState = () => { const classes = useStyles(); return ( Components can implement APIs that are displayed on this page. You diff --git a/plugins/api-docs/src/catalog/MissingImplementsApisEmptyState/index.ts b/plugins/api-docs/src/components/EmptyState/index.ts similarity index 78% rename from plugins/api-docs/src/catalog/MissingImplementsApisEmptyState/index.ts rename to plugins/api-docs/src/components/EmptyState/index.ts index 1b7d35c0a2..d195c43eb1 100644 --- a/plugins/api-docs/src/catalog/MissingImplementsApisEmptyState/index.ts +++ b/plugins/api-docs/src/components/EmptyState/index.ts @@ -14,4 +14,5 @@ * limitations under the License. */ -export { MissingImplementsApisEmptyState } from './MissingImplementsApisEmptyState'; +export { MissingConsumesApisEmptyState } from './MissingConsumesApisEmptyState'; +export { MissingProvidesApisEmptyState } from './MissingProvidesApisEmptyState'; diff --git a/plugins/api-docs/src/components/index.ts b/plugins/api-docs/src/components/index.ts index cf9b189091..cfd985f47c 100644 --- a/plugins/api-docs/src/components/index.ts +++ b/plugins/api-docs/src/components/index.ts @@ -14,13 +14,9 @@ * limitations under the License. */ -export type { ApiDefinitionWidget } from './ApiDefinitionCard'; -export { - ApiDefinitionCard, - defaultDefinitionWidgets, -} from './ApiDefinitionCard'; -export { AsyncApiDefinitionWidget } from './AsyncApiDefinitionWidget'; -export { OpenApiDefinitionWidget } from './OpenApiDefinitionWidget'; -export { PlainApiDefinitionWidget } from './PlainApiDefinitionWidget'; -export { useComponentApiNames } from './useComponentApiNames'; -export { useComponentApiEntities } from './useComponentApiEntities'; +export * from './ApiDefinitionCard'; +export * from './ApisCards'; +export * from './AsyncApiDefinitionWidget'; +export * from './ComponentsCards'; +export * from './OpenApiDefinitionWidget'; +export * from './PlainApiDefinitionWidget'; diff --git a/plugins/api-docs/src/components/useComponentApiEntities.ts b/plugins/api-docs/src/components/useComponentApiEntities.ts deleted file mode 100644 index 9e5cbd968e..0000000000 --- a/plugins/api-docs/src/components/useComponentApiEntities.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { useAsyncRetry } from 'react-use'; -import { errorApiRef, useApi } from '@backstage/core'; -import { - ApiEntity, - ComponentEntity, - parseEntityName, -} from '@backstage/catalog-model'; -import { catalogApiRef } from '@backstage/plugin-catalog'; -import { useComponentApiNames } from './useComponentApiNames'; - -export function useComponentApiEntities({ - entity, -}: { - entity: ComponentEntity; -}): { - loading: boolean; - apiEntities?: Map; - error?: Error; - retry: () => void; -} { - const catalogApi = useApi(catalogApiRef); - const errorApi = useApi(errorApiRef); - - const apiNames = useComponentApiNames(entity); - - const { loading, value: apiEntities, retry, error } = useAsyncRetry< - Map - >(async () => { - const resultMap = new Map(); - - await Promise.all( - apiNames.map(async name => { - try { - const apiEntityName = parseEntityName(name, { - defaultNamespace: entity.metadata.namespace, - defaultKind: 'API', - }); - - if (apiEntityName.kind !== 'API') { - throw new Error( - `Referenced entity of kind "${apiEntityName.kind}" as an API`, - ); - } - - const api = (await catalogApi.getEntityByName(apiEntityName)) as - | ApiEntity - | undefined; - - if (api) { - resultMap.set(api.metadata.name, api); - } - } catch (e) { - errorApi.post(e); - } - }), - ); - - return resultMap; - }, [catalogApi, entity]); - - return { - apiEntities, - loading, - error, - retry, - }; -} diff --git a/plugins/api-docs/src/components/useRelatedEntities.ts b/plugins/api-docs/src/components/useRelatedEntities.ts new file mode 100644 index 0000000000..847ec30578 --- /dev/null +++ b/plugins/api-docs/src/components/useRelatedEntities.ts @@ -0,0 +1,51 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Entity } from '@backstage/catalog-model'; +import { useApi } from '@backstage/core'; +import { catalogApiRef } from '@backstage/plugin-catalog'; +import { useAsyncRetry } from 'react-use'; + +// TODO: Maybe this hook is interesting for others too? +export function useRelatedEntities( + entity: Entity, + type: string, +): { + entities: (Entity | undefined)[] | undefined; + loading: boolean; + error: Error | undefined; +} { + const catalogApi = useApi(catalogApiRef); + const { loading, value, error } = useAsyncRetry< + (Entity | undefined)[] + >(async () => { + const relations = + entity.relations && entity.relations.filter(r => r.type === type); + + if (!relations) { + return []; + } + + return await Promise.all( + relations?.map(r => catalogApi.getEntityByName(r.target)), + ); + }, [entity, type]); + + return { + entities: value, + loading, + error, + }; +} diff --git a/plugins/api-docs/src/index.ts b/plugins/api-docs/src/index.ts index dbb32cee7b..f09aeb1038 100644 --- a/plugins/api-docs/src/index.ts +++ b/plugins/api-docs/src/index.ts @@ -14,6 +14,5 @@ * limitations under the License. */ -export * from './catalog'; export * from './components'; export { plugin } from './plugin'; diff --git a/plugins/api-docs/src/routes.ts b/plugins/api-docs/src/routes.ts index 6adff78e47..64277b9ae8 100644 --- a/plugins/api-docs/src/routes.ts +++ b/plugins/api-docs/src/routes.ts @@ -23,9 +23,3 @@ export const rootRoute = createRouteRef({ path: '/api-docs', title: 'APIs', }); - -export const catalogRoute = createRouteRef({ - icon: NoIcon, - path: '', - title: 'API', -}); diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 6d2fdcb67c..d64a58e017 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -10,6 +10,15 @@ "main": "dist/index.cjs.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/app-backend" + }, + "keywords": [ + "backstage" + ], "scripts": { "start": "backstage-cli backend:dev", "build": "backstage-cli backend:build", diff --git a/plugins/auth-backend/README.md b/plugins/auth-backend/README.md index e69bb5b86b..ee6e84cded 100644 --- a/plugins/auth-backend/README.md +++ b/plugins/auth-backend/README.md @@ -25,7 +25,7 @@ export AUTH_GOOGLE_CLIENT_ID=x export AUTH_GOOGLE_CLIENT_SECRET=x ``` -### Github +### GitHub #### Creating a GitHub OAuth application @@ -42,7 +42,7 @@ export AUTH_GITHUB_CLIENT_ID=x export AUTH_GITHUB_CLIENT_SECRET=x ``` -for github enterprise: +For GitHub Enterprise: ```bash export AUTH_GITHUB_CLIENT_ID=x @@ -50,7 +50,7 @@ export AUTH_GITHUB_CLIENT_SECRET=x export AUTH_GITHUB_ENTERPRISE_INSTANCE_URL=https://x ``` -### Gitlab +### GitLab #### Creating a GitLab OAuth application @@ -70,7 +70,7 @@ Follow this link, [Add new application](https://gitlab.com/-/profile/application ```bash export GITLAB_BASE_URL=https://gitlab.com -export AUTH_GITLAB_CLIENT_ID=x # Gitlab calls this the Application ID +export AUTH_GITLAB_CLIENT_ID=x # GitLab calls this the Application ID export AUTH_GITLAB_CLIENT_SECRET=x ``` diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index ddca6a24b4..5440a1342d 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -10,6 +10,15 @@ "main": "dist/index.cjs.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/auth-backend" + }, + "keywords": [ + "backstage" + ], "scripts": { "start": "backstage-cli backend:dev", "build": "backstage-cli backend:build", diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 69f260d020..19947d65f7 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -10,6 +10,15 @@ "main": "dist/index.cjs.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/catalog-backend" + }, + "keywords": [ + "backstage" + ], "scripts": { "start": "backstage-cli backend:dev", "build": "backstage-cli backend:build", diff --git a/plugins/catalog-graphql/package.json b/plugins/catalog-graphql/package.json index 9b12250ca0..3f28c023fd 100644 --- a/plugins/catalog-graphql/package.json +++ b/plugins/catalog-graphql/package.json @@ -9,6 +9,16 @@ "main": "dist/index.cjs.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/catalog-graphql" + }, + "keywords": [ + "backstage", + "graphql" + ], "scripts": { "start": "backstage-cli backend:dev", "build": "backstage-cli backend:build", diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 74cb3635be..9f5f9b9913 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -10,6 +10,15 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/catalog-import" + }, + "keywords": [ + "backstage" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 6887f71b3b..93d8638353 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -10,6 +10,15 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/catalog" + }, + "keywords": [ + "backstage" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index c67696244c..d62dac8d21 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -10,6 +10,16 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/circleci" + }, + "keywords": [ + "backstage", + "circleci" + ], "scripts": { "build": "backstage-cli plugin:build", "lint": "backstage-cli lint", diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 3e514a5eb8..8e52b29442 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -9,6 +9,16 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/cloudbuild" + }, + "keywords": [ + "backstage", + "google cloud" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 9e0f890fcd..f4db2c98e0 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -10,6 +10,15 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/cost-insights" + }, + "keywords": [ + "backstage" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/explore/package.json b/plugins/explore/package.json index 45cbb310f8..abf730a66f 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -10,6 +10,15 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/explore" + }, + "keywords": [ + "backstage" + ], "scripts": { "build": "backstage-cli plugin:build", "lint": "backstage-cli lint", diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index faa79ed647..95a226ef3a 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -9,6 +9,16 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/gcp-projects" + }, + "keywords": [ + "backstage", + "google cloud" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 5822a0e608..7f4999f107 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -10,6 +10,17 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/github-actions" + }, + "keywords": [ + "backstage", + "github", + "github actions" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/github-actions/src/components/Cards/RecentWorkflowRunsCard.tsx b/plugins/github-actions/src/components/Cards/RecentWorkflowRunsCard.tsx index 46f60981a0..70348ece0a 100644 --- a/plugins/github-actions/src/components/Cards/RecentWorkflowRunsCard.tsx +++ b/plugins/github-actions/src/components/Cards/RecentWorkflowRunsCard.tsx @@ -60,7 +60,7 @@ export const RecentWorkflowRunsCard = ({ { - try { - let build; - if (branch) { - build = await api.getLastBuild(`${owner}/${repo}/${branch}`); - } else { - build = await api.getFolder(`${owner}/${repo}`); - } - return build; - } catch (e) { - errorApi.post(e); - return Promise.reject(e); - } - }, [api, branch, errorApi, owner, repo]); - const restartBuild = async (buildName: string) => { try { await api.retry(buildName); @@ -49,18 +34,24 @@ export function useBuilds(owner: string, repo: string, branch?: string) { } }; - useEffect(() => { - getBuilds().then(b => { - const size = Array.isArray(b) ? b?.[0].build_num! : 1; - setTotal(size); - }); - }, [repo, getBuilds]); + const { loading, value: builds, retry } = useAsyncRetry(async () => { + try { + let builds; + if (branch) { + builds = await api.getLastBuild(`${owner}/${repo}/${branch}`); + } else { + builds = await api.getFolder(`${owner}/${repo}`); + } - const { loading, value: builds, retry } = useAsyncRetry( - () => - getBuilds().then(retrievedBuilds => retrievedBuilds ?? [], restartBuild), - [page, pageSize, getBuilds], - ); + const size = Array.isArray(builds) ? builds?.[0].build_num! : 1; + setTotal(size); + + return builds || []; + } catch (e) { + errorApi.post(e); + throw e; + } + }, [api, errorApi, owner, repo, branch]); const projectName = `${owner}/${repo}`; return [ diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 9d1ff2eed4..92439b8994 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -10,6 +10,16 @@ "main": "dist/index.cjs.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/kubernetes-backend" + }, + "keywords": [ + "backstage", + "kubernetes" + ], "configSchema": "schema.d.ts", "scripts": { "start": "backstage-cli backend:dev", diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 6fa2f7e3ff..5fbb764092 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -9,6 +9,16 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/kubernetes" + }, + "keywords": [ + "backstage", + "kubernetes" + ], "configSchema": "schema.d.ts", "scripts": { "build": "backstage-cli plugin:build", diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index da824044a9..34e2bff874 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -10,6 +10,16 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/lighthouse" + }, + "keywords": [ + "backstage", + "lighthouse" + ], "scripts": { "build": "backstage-cli plugin:build", "lint": "backstage-cli lint", diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index 9cfb810f4a..952a8c2388 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -10,6 +10,16 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/newrelic" + }, + "keywords": [ + "backstage", + "newrelic" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index 4d58a1da42..d7e2fd7b6f 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -9,6 +9,16 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/pagerduty" + }, + "keywords": [ + "backstage", + "pagerduty" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 68c89169c5..5929dd55d0 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -9,6 +9,15 @@ "main": "dist/index.cjs.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/proxy-backend" + }, + "keywords": [ + "backstage" + ], "scripts": { "start": "backstage-cli backend:dev", "build": "backstage-cli backend:build", diff --git a/plugins/register-component/package.json b/plugins/register-component/package.json index 18a97e36f7..f40f86af5a 100644 --- a/plugins/register-component/package.json +++ b/plugins/register-component/package.json @@ -10,6 +10,15 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/register-component" + }, + "keywords": [ + "backstage" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index edaaa3a9b5..74f83cb0d3 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -10,6 +10,16 @@ "main": "dist/index.cjs.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/rollbar-backend" + }, + "keywords": [ + "backstage", + "rollbar" + ], "scripts": { "start": "backstage-cli backend:dev", "build": "backstage-cli backend:build", diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 9cdd7895bf..bf60120486 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -10,6 +10,16 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/rollbar" + }, + "keywords": [ + "backstage", + "rollbar" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 1b22ee1f14..de8e1c0618 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -72,7 +72,7 @@ - 991a950e0: Added .fromConfig static factories for Preparers and Publishers + read integrations config to support url location types - c926765a2: Allow templates to be located on non-default branch - 6840a68df: Add authentication token to Scaffolder GitHub Preparer -- 1c8c43756: The new `scaffolder.github.baseUrl` config property allows to specify a custom base url for GitHub enterprise instances +- 1c8c43756: The new `scaffolder.github.baseUrl` config property allows to specify a custom base url for GitHub Enterprise instances - 5e4551e3a: Added support for configuring the working directory of the Scaffolder: ```yaml diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 7aa8fc873a..3cfcee3fd9 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -10,6 +10,15 @@ "main": "dist/index.cjs.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/scaffolder-backend" + }, + "keywords": [ + "backstage" + ], "scripts": { "start": "backstage-cli backend:dev", "build": "backstage-cli backend:build", diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml b/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml index 10109dee3d..e3eea846f6 100644 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml +++ b/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml @@ -29,7 +29,7 @@ spec: type: string description: Help others understand what this website is for. use_typescript: - title: Use Typescript + title: Use TypeScript type: boolean - description: Include typescript + description: Include TypeScript default: true diff --git a/plugins/scaffolder-backend/src/service/router.test.ts b/plugins/scaffolder-backend/src/service/router.test.ts index c2b60a2040..633d16b2a4 100644 --- a/plugins/scaffolder-backend/src/service/router.test.ts +++ b/plugins/scaffolder-backend/src/service/router.test.ts @@ -172,8 +172,8 @@ describe('createRouter', () => { }, use_typescript: { default: true, - description: 'Include typescript', - title: 'Use Typescript', + description: 'Include TypeScript', + title: 'Use TypeScript', type: 'boolean', }, }, diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 197d54a9a4..b10af626e5 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -99,7 +99,7 @@ ![failed-to-create-component](https://user-images.githubusercontent.com/33940798/94339296-90969400-0016-11eb-9a74-ce16b3dd8d88.gif) - c5ef12926: fix the accordion details design when job stage fail -- 1c8c43756: The new `scaffolder.github.baseUrl` config property allows to specify a custom base url for GitHub enterprise instances +- 1c8c43756: The new `scaffolder.github.baseUrl` config property allows to specify a custom base url for GitHub Enterprise instances - Updated dependencies [28edd7d29] - Updated dependencies [819a70229] - Updated dependencies [3a4236570] diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 381f2bfdbf..869e0ef09c 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -10,6 +10,15 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/scaffolder" + }, + "keywords": [ + "backstage" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/search/package.json b/plugins/search/package.json index 490d5003ef..5e34731cdb 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -9,6 +9,15 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/search" + }, + "keywords": [ + "backstage" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/sentry/dev/index.tsx b/plugins/sentry/dev/index.tsx index 812a5585d4..fc75008040 100644 --- a/plugins/sentry/dev/index.tsx +++ b/plugins/sentry/dev/index.tsx @@ -14,7 +14,94 @@ * limitations under the License. */ +import { Entity } from '@backstage/catalog-model'; +import { + Content, + createPlugin, + createRouteRef, + Header, + Page, +} from '@backstage/core'; import { createDevApp } from '@backstage/dev-utils'; -import { plugin } from '../src/plugin'; +import { Grid } from '@material-ui/core'; +import React from 'react'; +import { + MockSentryApi, + SentryApi, + sentryApiRef, + SentryIssuesWidget, +} from '../src'; +import { SENTRY_PROJECT_SLUG_ANNOTATION } from '../src/components/useProjectSlug'; -createDevApp().registerPlugin(plugin).render(); +createDevApp() + .registerApi({ + api: sentryApiRef, + deps: {}, + factory: () => + ({ + fetchIssues: async (project: string) => { + switch (project) { + case 'error': + throw new Error('Error!'); + + case 'never': + return new Promise(() => {}); + + case 'with-values': + return new MockSentryApi().fetchIssues(); + + default: + return []; + } + }, + } as SentryApi), + }) + .registerPlugin( + createPlugin({ + id: 'sentry-demo', + register({ router }) { + const entity = (name?: string) => + ({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + annotations: { + [SENTRY_PROJECT_SLUG_ANNOTATION]: name, + }, + name: name, + }, + } as Entity); + + const ExamplePage = () => ( + +
+ + + + + + + + + + + + + + + + + + + + + ); + + router.addRoute( + createRouteRef({ path: '/', title: 'Sentry' }), + ExamplePage, + ); + }, + }), + ) + .render(); diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index c9708ee875..6b235243ea 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -10,6 +10,16 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/sentry" + }, + "keywords": [ + "backstage", + "sentry" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/sonarqube/dev/index.tsx b/plugins/sonarqube/dev/index.tsx index 812a5585d4..830d8aa575 100644 --- a/plugins/sonarqube/dev/index.tsx +++ b/plugins/sonarqube/dev/index.tsx @@ -14,7 +14,149 @@ * limitations under the License. */ +import { Entity } from '@backstage/catalog-model'; +import { + Content, + createPlugin, + createRouteRef, + Header, + Page, +} from '@backstage/core'; import { createDevApp } from '@backstage/dev-utils'; -import { plugin } from '../src/plugin'; +import { Grid } from '@material-ui/core'; +import React from 'react'; +import { SonarQubeCard } from '../src'; +import { FindingSummary, SonarQubeApi, sonarQubeApiRef } from '../src/api'; +import { SONARQUBE_PROJECT_KEY_ANNOTATION } from '../src/components/useProjectKey'; -createDevApp().registerPlugin(plugin).render(); +createDevApp() + .registerApi({ + api: sonarQubeApiRef, + deps: {}, + factory: () => + ({ + getFindingSummary: async componentKey => { + switch (componentKey) { + case 'error': + throw new Error('Error!'); + + case 'never': + return new Promise(() => {}); + + case 'not-computed': + return { + lastAnalysis: new Date().toISOString(), + metrics: { + bugs: '0', + reliability_rating: '1.0', + vulnerabilities: '0', + security_rating: '1.0', + code_smells: '0', + sqale_rating: '1.0', + coverage: '0.0', + duplicated_lines_density: '0.0', + }, + projectUrl: `/#${componentKey}`, + getIssuesUrl: i => `/#${componentKey}/issues/${i}`, + getComponentMeasuresUrl: i => `/#${componentKey}/measures/${i}`, + } as FindingSummary; + + case 'failed': + return { + lastAnalysis: new Date().toISOString(), + metrics: { + alert_status: 'FAILED', + bugs: '4', + reliability_rating: '2.0', + vulnerabilities: '18', + security_rating: '3.0', + code_smells: '22', + sqale_rating: '5.0', + coverage: '15.7', + duplicated_lines_density: '15.6', + }, + projectUrl: `/#${componentKey}`, + getIssuesUrl: i => `/#${componentKey}/issues/${i}`, + getComponentMeasuresUrl: i => `/#${componentKey}/measures/${i}`, + } as FindingSummary; + + case 'passed': + return { + lastAnalysis: new Date().toISOString(), + metrics: { + alert_status: 'OK', + bugs: '0', + reliability_rating: '1.0', + vulnerabilities: '0', + security_rating: '1.0', + code_smells: '0', + sqale_rating: '1.0', + coverage: '100.0', + duplicated_lines_density: '0.0', + }, + projectUrl: `/#${componentKey}`, + getIssuesUrl: i => `/#${componentKey}/issues/${i}`, + getComponentMeasuresUrl: i => `/#${componentKey}/measures/${i}`, + } as FindingSummary; + + default: + return undefined; + } + }, + } as SonarQubeApi), + }) + .registerPlugin( + createPlugin({ + id: 'defectdojo-demo', + register({ router }) { + const entity = (name?: string) => + ({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + annotations: { + [SONARQUBE_PROJECT_KEY_ANNOTATION]: name, + }, + name: name, + }, + } as Entity); + + const ExamplePage = () => ( + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + ); + + router.addRoute( + createRouteRef({ path: '/', title: 'SonarQube' }), + ExamplePage, + ); + }, + }), + ) + .render(); diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index 2dd52ab021..46168fe617 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -10,6 +10,17 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/sonarqube" + }, + "keywords": [ + "backstage", + "sonarqube", + "sonarcloud" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/sonarqube/src/api/SonarQubeApi.ts b/plugins/sonarqube/src/api/SonarQubeApi.ts new file mode 100644 index 0000000000..5734f9a3ca --- /dev/null +++ b/plugins/sonarqube/src/api/SonarQubeApi.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createApiRef } from '@backstage/core'; +import { MetricKey, SonarUrlProcessorFunc } from './types'; + +/** + * Define a type to make sure that all metrics are used + */ +export type Metrics = { + [key in MetricKey]: string | undefined; +}; + +export interface FindingSummary { + lastAnalysis: string; + metrics: Metrics; + projectUrl: string; + getIssuesUrl: SonarUrlProcessorFunc; + getComponentMeasuresUrl: SonarUrlProcessorFunc; +} + +export const sonarQubeApiRef = createApiRef({ + id: 'plugin.sonarqube.service', + description: 'Used by the SonarQube plugin to make requests', +}); + +export type SonarQubeApi = { + getFindingSummary(componentKey?: string): Promise; +}; diff --git a/plugins/sonarqube/src/api/index.test.ts b/plugins/sonarqube/src/api/SonarQubeClient.test.ts similarity index 96% rename from plugins/sonarqube/src/api/index.test.ts rename to plugins/sonarqube/src/api/SonarQubeClient.test.ts index 2d96fc2135..a3ae24de52 100644 --- a/plugins/sonarqube/src/api/index.test.ts +++ b/plugins/sonarqube/src/api/SonarQubeClient.test.ts @@ -18,12 +18,12 @@ import { UrlPatternDiscovery } from '@backstage/core'; import { msw } from '@backstage/test-utils'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; -import { FindingSummary, SonarQubeApi } from './index'; +import { FindingSummary, SonarQubeClient } from './index'; import { ComponentWrapper, MeasuresWrapper } from './types'; const server = setupServer(); -describe('SonarQubeApi', () => { +describe('SonarQubeClient', () => { msw.setupDefaultHandlers(server); const mockBaseUrl = 'http://backstage:9191/api/proxy'; @@ -111,7 +111,7 @@ describe('SonarQubeApi', () => { it('should report finding summary', async () => { setupHandlers(); - const client = new SonarQubeApi({ discoveryApi }); + const client = new SonarQubeClient({ discoveryApi }); const summary = await client.getFindingSummary('our-service'); expect(summary).toEqual( @@ -142,7 +142,7 @@ describe('SonarQubeApi', () => { it('should report finding summary (custom baseUrl)', async () => { setupHandlers(); - const client = new SonarQubeApi({ + const client = new SonarQubeClient({ discoveryApi, baseUrl: 'http://a.instance.local', }); diff --git a/plugins/sonarqube/src/api/SonarQubeClient.ts b/plugins/sonarqube/src/api/SonarQubeClient.ts new file mode 100644 index 0000000000..893f32b0a6 --- /dev/null +++ b/plugins/sonarqube/src/api/SonarQubeClient.ts @@ -0,0 +1,101 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DiscoveryApi } from '@backstage/core'; +import fetch from 'cross-fetch'; +import { FindingSummary, Metrics, SonarQubeApi } from './SonarQubeApi'; +import { ComponentWrapper, MeasuresWrapper } from './types'; + +export class SonarQubeClient implements SonarQubeApi { + discoveryApi: DiscoveryApi; + baseUrl: string; + + constructor({ + discoveryApi, + baseUrl = 'https://sonarcloud.io/', + }: { + discoveryApi: DiscoveryApi; + baseUrl?: string; + }) { + this.discoveryApi = discoveryApi; + this.baseUrl = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`; + } + + private async callApi(path: string): Promise { + const apiUrl = `${await this.discoveryApi.getBaseUrl('proxy')}/sonarqube`; + const response = await fetch(`${apiUrl}/${path}`); + if (response.status === 200) { + return (await response.json()) as T; + } + return undefined; + } + + async getFindingSummary( + componentKey?: string, + ): Promise { + if (!componentKey) { + return undefined; + } + + const component = await this.callApi( + `components/show?component=${componentKey}`, + ); + if (!component) { + return undefined; + } + + const metrics: Metrics = { + alert_status: undefined, + bugs: undefined, + reliability_rating: undefined, + vulnerabilities: undefined, + security_rating: undefined, + code_smells: undefined, + sqale_rating: undefined, + coverage: undefined, + duplicated_lines_density: undefined, + }; + + const measures = await this.callApi( + `measures/search?projectKeys=${componentKey}&metricKeys=${Object.keys( + metrics, + ).join(',')}`, + ); + if (!measures) { + return undefined; + } + + measures.measures + .filter(m => m.component === componentKey) + .forEach(m => { + metrics[m.metric] = m.value; + }); + + return { + lastAnalysis: component.component.analysisDate, + metrics, + projectUrl: `${this.baseUrl}dashboard?id=${componentKey}`, + getIssuesUrl: identifier => + `${ + this.baseUrl + }project/issues?id=${componentKey}&types=${identifier.toUpperCase()}&resolved=false`, + getComponentMeasuresUrl: (identifier: string) => + `${ + this.baseUrl + }component_measures?id=${componentKey}&metric=${identifier.toLowerCase()}&resolved=false&view=list`, + }; + } +} diff --git a/plugins/sonarqube/src/api/index.ts b/plugins/sonarqube/src/api/index.ts index f2c616f53c..8442465dee 100644 --- a/plugins/sonarqube/src/api/index.ts +++ b/plugins/sonarqube/src/api/index.ts @@ -14,112 +14,6 @@ * limitations under the License. */ -import { createApiRef, DiscoveryApi } from '@backstage/core'; -import fetch from 'cross-fetch'; -import { - ComponentWrapper, - MeasuresWrapper, - MetricKey, - SonarUrlProcessorFunc, -} from './types'; - -/** - * Define a type to make sure that all metrics are used - */ -type Metrics = { - [key in MetricKey]: string | undefined; -}; - -export interface FindingSummary { - lastAnalysis: string; - metrics: Metrics; - projectUrl: string; - getIssuesUrl: SonarUrlProcessorFunc; - getComponentMeasuresUrl: SonarUrlProcessorFunc; -} - -export const sonarQubeApiRef = createApiRef({ - id: 'plugin.sonarqube.service', - description: 'Used by the SonarQube plugin to make requests', -}); - -export class SonarQubeApi { - discoveryApi: DiscoveryApi; - baseUrl: string; - - constructor({ - discoveryApi, - baseUrl = 'https://sonarcloud.io/', - }: { - discoveryApi: DiscoveryApi; - baseUrl?: string; - }) { - this.discoveryApi = discoveryApi; - this.baseUrl = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`; - } - - private async callApi(path: string): Promise { - const apiUrl = `${await this.discoveryApi.getBaseUrl('proxy')}/sonarqube`; - const response = await fetch(`${apiUrl}/${path}`); - if (response.status === 200) { - return (await response.json()) as T; - } - return undefined; - } - - async getFindingSummary( - componentKey?: string, - ): Promise { - if (!componentKey) { - return undefined; - } - - const component = await this.callApi( - `components/show?component=${componentKey}`, - ); - if (!component) { - return undefined; - } - - const metrics: Metrics = { - alert_status: undefined, - bugs: undefined, - reliability_rating: undefined, - vulnerabilities: undefined, - security_rating: undefined, - code_smells: undefined, - sqale_rating: undefined, - coverage: undefined, - duplicated_lines_density: undefined, - }; - - const measures = await this.callApi( - `measures/search?projectKeys=${componentKey}&metricKeys=${Object.keys( - metrics, - ).join(',')}`, - ); - if (!measures) { - return undefined; - } - - measures.measures - .filter(m => m.component === componentKey) - .forEach(m => { - metrics[m.metric] = m.value; - }); - - return { - lastAnalysis: component.component.analysisDate, - metrics, - projectUrl: `${this.baseUrl}dashboard?id=${componentKey}`, - getIssuesUrl: identifier => - `${ - this.baseUrl - }project/issues?id=${componentKey}&types=${identifier.toUpperCase()}&resolved=false`, - getComponentMeasuresUrl: (identifier: string) => - `${ - this.baseUrl - }component_measures?id=${componentKey}&metric=${identifier.toLowerCase()}&resolved=false&view=list`, - }; - } -} +export type { Metrics, FindingSummary, SonarQubeApi } from './SonarQubeApi'; +export { sonarQubeApiRef } from './SonarQubeApi'; +export { SonarQubeClient } from './SonarQubeClient'; diff --git a/plugins/sonarqube/src/plugin.ts b/plugins/sonarqube/src/plugin.ts index 154d4ee3b8..f8b8cafc5c 100644 --- a/plugins/sonarqube/src/plugin.ts +++ b/plugins/sonarqube/src/plugin.ts @@ -20,7 +20,7 @@ import { createPlugin, discoveryApiRef, } from '@backstage/core'; -import { SonarQubeApi, sonarQubeApiRef } from './api'; +import { sonarQubeApiRef, SonarQubeClient } from './api'; export const plugin = createPlugin({ id: 'sonarqube', @@ -29,7 +29,7 @@ export const plugin = createPlugin({ api: sonarQubeApiRef, deps: { configApi: configApiRef, discoveryApi: discoveryApiRef }, factory: ({ configApi, discoveryApi }) => - new SonarQubeApi({ + new SonarQubeClient({ discoveryApi, baseUrl: configApi.getOptionalString('sonarQube.baseUrl'), }), diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 269e0e0b4f..740ab24887 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -10,6 +10,15 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/tech-radar" + }, + "keywords": [ + "backstage" + ], "scripts": { "build": "backstage-cli plugin:build", "lint": "backstage-cli lint", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index 6557dfe924..77f8ba7a98 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -4,7 +4,7 @@ ### Patch Changes -- ae95c7ff3: Update URL auth format for Gitlab clone +- ae95c7ff3: Update URL auth format for GitLab clone - Updated dependencies [612368274] - Updated dependencies [08835a61d] - Updated dependencies [a9fd599f7] @@ -67,7 +67,7 @@ Draft until we're happy with the implementation, then I can add more docs and changelog entry. Also didn't go on a thorough hunt for places where discovery can be used, but I don't think there are many since it's been pretty awkward to do service-to-service communication. -- 5a920c6e4: Updated naming of environment variables. New pattern [NAME]\_TOKEN for Github, Gitlab, Azure & Github enterprise access tokens. +- 5a920c6e4: Updated naming of environment variables. New pattern [NAME]\_TOKEN for GitHub, GitLab, Azure & GitHub Enterprise access tokens. ### Detail: diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index cba357f371..8df7a3ce65 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -10,6 +10,16 @@ "main": "dist/index.cjs.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/techdocs-backend" + }, + "keywords": [ + "backstage", + "techdocs" + ], "scripts": { "start": "backstage-cli backend:dev", "build": "backstage-cli backend:build", diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 5b95bd64d2..ac66a736d7 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -10,6 +10,16 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/techdocs" + }, + "keywords": [ + "backstage", + "techdocs" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index e1b94e85d9..493f9b646b 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -10,6 +10,15 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/user-settings" + }, + "keywords": [ + "backstage" + ], "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", diff --git a/plugins/user-settings/src/components/AuthProviders/DefaultProviderSettings.tsx b/plugins/user-settings/src/components/AuthProviders/DefaultProviderSettings.tsx index a1dab1e7d4..415d1eaad6 100644 --- a/plugins/user-settings/src/components/AuthProviders/DefaultProviderSettings.tsx +++ b/plugins/user-settings/src/components/AuthProviders/DefaultProviderSettings.tsx @@ -49,7 +49,7 @@ export const DefaultProviderSettings = ({ configuredProviders }: Props) => ( )} {configuredProviders.includes('github') && ( ( )} {configuredProviders.includes('gitlab') && (