This commit is contained in:
Tim Hansen
2020-08-31 20:37:53 -06:00
247 changed files with 3877 additions and 2093 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
---
name: 'Feature Request'
about: 'Suggest new features and changes'
labels: help wanted
labels: enhancement
---
<!--- Provide a general summary of the feature request in the Title above -->
-8
View File
@@ -33,19 +33,11 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: cache all node_modules
id: cache-modules
uses: actions/cache@v2
with:
path: '**/node_modules'
key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }}
- name: find location of global yarn cache
id: yarn-cache
if: steps.cache-modules.outputs.cache-hit != 'true'
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: cache global yarn cache
uses: actions/cache@v2
if: steps.cache-modules.outputs.cache-hit != 'true'
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
-8
View File
@@ -25,19 +25,11 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: cache all node_modules
id: cache-modules
uses: actions/cache@v2
with:
path: '**/node_modules'
key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }}
- name: find location of global yarn cache
id: yarn-cache
if: steps.cache-modules.outputs.cache-hit != 'true'
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: cache global yarn cache
uses: actions/cache@v2
if: steps.cache-modules.outputs.cache-hit != 'true'
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
+8
View File
@@ -74,3 +74,11 @@ jobs:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
package_root: "packages/core"
tag_prefix: "v"
- name: Discord notification
if: ${{ failure() }}
uses: Ilshidur/action-discord@0.2.0
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
with:
args: 'Master build failed https://github.com/{{GITHUB_REPOSITORY}}/actions/runs/{{GITHUB_RUN_ID}}'
+4
View File
@@ -8,6 +8,10 @@ If you encounter issues while upgrading to a newer version, don't hesitate to re
> Collect changes for the next release below
## v0.1.1-alpha.20
- Includes https://github.com/spotify/backstage/pull/2097 to resolve issues with create-plugin command.
## v0.1.1-alpha.19
### @backstage/create-app
+44 -12
View File
@@ -14,21 +14,15 @@ backend:
client: sqlite3
connection: ':memory:'
# See README.md in the proxy-backend plugin for information on the configuration format
proxy:
'/circleci/api':
target: 'https://circleci.com/api/v1.1'
changeOrigin: true
pathRewrite:
'^/proxy/circleci/api/': '/'
'/circleci/api': https://circleci.com/api/v1.1
'/jenkins/api':
target: 'http://localhost:8080'
changeOrigin: true
target: http://localhost:8080
headers:
Authorization:
$secret:
env: JENKINS_BASIC_AUTH_HEADER
pathRewrite:
'^/proxy/jenkins/api/': '/'
organization:
name: Spotify
@@ -50,6 +44,46 @@ newrelic:
baseUrl: 'https://api.newrelic.com/v2'
key: NEW_RELIC_REST_API_KEY
lighthouse:
baseUrl: http://localhost:3003
catalog:
processors:
githubApi:
privateToken:
$secret:
env: GITHUB_PRIVATE_TOKEN
bitbucketApi:
username:
$secret:
env: BITBUCKET_USERNAME
appPassword:
$secret:
env: BITBUCKET_APP_PASSWORD
gitlabApi:
privateToken:
$secret:
env: GITLAB_PRIVATE_TOKEN
azureApi:
privateToken:
$secret:
env: AZURE_PRIVATE_TOKEN
exampleEntityLocations:
github:
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/playback-order-component.yaml
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/podcast-api-component.yaml
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/queue-proxy-component.yaml
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/searcher-component.yaml
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/playback-lib-component.yaml
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/www-artist-component.yaml
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/shuffle-api-component.yaml
- https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml
- https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml
- https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml
- https://github.com/spotify/cookiecutter-golang/blob/master/template.yaml
- https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml
auth:
providers:
google:
@@ -124,8 +158,6 @@ auth:
env: AUTH_AUTH0_DOMAIN
microsoft:
development:
appOrigin: "http://localhost:3000/"
secure: false
clientId:
$secret:
env: AUTH_MICROSOFT_CLIENT_ID
@@ -134,4 +166,4 @@ auth:
env: AUTH_MICROSOFT_CLIENT_SECRET
tenantId:
$secret:
env: AUTH_MICROSOFT_TENANT_ID
env: AUTH_MICROSOFT_TENANT_ID
+2 -2
View File
@@ -3,7 +3,7 @@ id: FAQ
title: FAQ
---
## Product FAQ:
## Product FAQ
### Can we call Backstage something different? So that it fits our company better?
@@ -67,7 +67,7 @@ valuable as you grow.
Yes! The Backstage UI is built using Material-UI. With the theming capabilities
of Material-UI, you are able to adapt the interface to your brand guidelines.
## Technical FAQ:
## Technical FAQ
### Why Material-UI?
+2 -104
View File
@@ -1,105 +1,3 @@
# Documentation structure
# Documentation
**Note!** This documentation structure is very much work in progress. If (when,
really 😆) you find broken links or missing content, please create an issue or,
better yet, a pull request.
# Plugins
- Overview
- [What is Backstage?](overview/what-is-backstage.md)
- [Backstage architecture](overview/architecture-overview.md)
- [Architecture and terminology](overview/architecture-terminology.md)
- [Roadmap](overview/roadmap.md)
- [Vision](overview/vision.md)
- [Strategies for adopting](overview/adopting.md)
- Getting started
- [Running Backstage locally](getting-started/index.md)
- [Installation](getting-started/installation.md)
- [Local development](getting-started/development-environment.md)
- [Demo deployment](https://backstage-demo.roadie.io)
- Production deployments
- [Create an App](getting-started/create-an-app.md)
- App configuration
- [Configuring App with plugins](getting-started/configure-app-with-plugins.md)
- [Customize the look-and-feel of your App](getting-started/app-custom-theme.md)
- Deployment scenarios
- [Kubernetes](getting-started/deployment-k8s.md)
- [Other](getting-started/deployment-other.md)
- Features
- Software Catalog
- [Overview](features/software-catalog/index.md)
- [System model](features/software-catalog/system-model.md)
- [YAML File Format](features/software-catalog/descriptor-format.md)
- [Extending the model](features/software-catalog/extending-the-model.md)
- [External integrations](features/software-catalog/external-integrations.md)
- [API](features/software-catalog/api.md)
- Software creation templates
- [Overview](features/software-templates/index.md)
- [Adding templates](features/software-templates/adding-templates.md)
- Extending the Scaffolder:
- [Overview](features/software-templates/extending/index.md)
- [Create your own Templater](features/software-templates/extending/create-your-own-templater.md)
- [Create your own Publisher](features/software-templates/extending/create-your-own-publisher.md)
- [Create your own Preparer](features/software-templates/extending/create-your-own-preparer.md)
- Docs-like-code
- [Overview](features/techdocs/README.md)
- [Getting Started](features/techdocs/getting-started.md)
- [Concepts](features/techdocs/concepts.md)
- [Creating and Publishing Documentation](features/techdocs/creating-and-publishing.md)
- [FAQ](features/techdocs/FAQ.md)
- Plugins
- [Overview](plugins/index.md)
- [Existing plugins](plugins/existing-plugins.md)
- [Creating a new plugin](plugins/create-a-plugin.md)
- [Developing a plugin](plugins/plugin-development.md)
- [Structure of a plugin](plugins/structure-of-a-plugin.md)
- Backends and APIs
- [Proxying](plugins/proxying.md)
- [Backstage backend plugin](plugins/backend-plugin.md)
- [Call existing API](plugins/call-existing-api.md)
- Testing
- [Overview](plugins/testing.md)
- Publishing
- [Open source and NPM](plugins/publishing.md)
- [Private/internal (non-open source)](plugins/publish-private.md)
- Configuration
- [Overview](conf/index.md)
- [Reading Configuration](conf/reading.md)
- [Writing Configuration](conf/writing.md)
- [Defining Configuration](conf/defining.md)
- Authentication and identity
- [Overview](auth/index.md)
- [Add auth provider](auth/add-auth-provider.md)
- [Auth backend](auth/auth-backend.md)
- [OAuth](auth/oauth.md)
- [Glossary](auth/glossary.md)
- Designing for Backstage
- [Backstage Design Language System (DLS)](dls/design.md)
- [Storybook -- reusable UI components](http://backstage.io/storybook)
- [Contributing to Storybook](dls/contributing-to-storybook.md)
- [Figma resources](dls/figma.md)
- API references
- TypeScript API
- [Utility APIs](api/utility-apis.md)
- [Utility API References](reference/utility-apis/README.md)
- [createPlugin](reference/createPlugin.md)
- [createPlugin-feature-flags](reference/createPlugin-feature-flags.md)
- [createPlugin-router](reference/createPlugin-router.md)
- Backend APIs
- [Backend](api/backend.md)
- Tutorials
- [Overview](tutorials/index.md)
- Architecture Decision Records (ADRs)
- [Overview](architecture-decisions/index.md)
- [ADR001 - Architecture Decision Record (ADR) log](architecture-decisions/adr001-add-adr-log.md)
- [ADR002 - Default Software Catalog File Format](architecture-decisions/adr002-default-catalog-file-format.md)
- [ADR003 - Avoid Default Exports and Prefer Named Exports](architecture-decisions/adr003-avoid-default-exports.md)
- [ADR004 - Module Export Structure](architecture-decisions/adr004-module-export-structure.md)
- [ADR005 - Catalog Core Entities](architecture-decisions/adr005-catalog-core-entities.md)
- [ADR006 - Avoid React.FC and React.SFC](architecture-decisions/adr006-avoid-react-fc.md)
- [ADR007 - Use MSW for Mocking Network Requests](architecture-decisions/adr007-use-msw-to-mock-service-requests.md)
- [ADR008 - Default Catalog File Name](architecture-decisions/adr008-default-catalog-file-name.md)
- [Contribute](../CONTRIBUTING.md)
- [Support](overview/support.md)
- [FAQ](FAQ.md)
The Backstage documentation is available at https://backstage.io/docs
+4 -3
View File
@@ -4,8 +4,6 @@ title: Architecture Decision Records (ADR)
sidebar_label: Overview
---
#
The substantial architecture decisions made in the Backstage project lives here.
For more information about ADRs, when to write them, and why, please see
[this blog post](https://engineering.atspotify.com/2020/04/14/when-should-i-write-an-architecture-decision-record/).
@@ -25,7 +23,10 @@ Records should be stored under the `architecture-decisions` directory.
- Submit a pull request
- Address and integrate feedback from the community
- Eventually, assign a number
- Add the full path of the ADR to the [`mkdocs.yml`](/mkdocs.yml)
- Add the path of the ADR to the microsite sidebar in
[`sidebars.json`](https://github.com/spotify/backstage/blob/master/microsite/sidebars.json)
- Add the path of the ADR to the
[`mkdocs.yml`](https://github.com/spotify/backstage/blob/master/mkdocs.yml)
- Merge the pull request
## Superseding an ADR

Before

Width:  |  Height:  |  Size: 303 KiB

After

Width:  |  Height:  |  Size: 303 KiB

Before

Width:  |  Height:  |  Size: 269 KiB

After

Width:  |  Height:  |  Size: 269 KiB

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Before

Width:  |  Height:  |  Size: 414 KiB

After

Width:  |  Height:  |  Size: 414 KiB

Before

Width:  |  Height:  |  Size: 234 KiB

After

Width:  |  Height:  |  Size: 234 KiB

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

+4 -1
View File
@@ -1,4 +1,7 @@
# Authentication Backend Classes Layout and Description
---
id: auth-backend-classes
title: Auth backend classes
---
## How Does Authentication Work?
+2 -2
View File
@@ -104,6 +104,8 @@ request an access token.
The following diagram visualizes the flow described in the previous section.
![](oauth-popup-flow.svg)
<!--
@startuml oauth-popup-flow
@@ -152,5 +154,3 @@ Browser <- Backend: Tokens and info
@enduml
-->
![](oauth-popup-flow.svg)
@@ -0,0 +1,57 @@
---
id: software-catalog-configuration
title: Catalog Configuration
---
## Static Location Configuration
To enable declarative catalog setups, it is possible to add locations to the
catalog via [static configuration](../../conf/index.md). Locations are added to
the catalog under the `catalog.locations` key, for example:
```yaml
catalog:
locations:
- type: github
target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml
```
The locations added through static configuration can not be removed through the
catalog locations API. To remove the locations, you have to remove them from the
configuration.
## Catalog Rules
By default the catalog will only allow ingestion of entities with the kind
`Component` and `API`. In order to allow entities of other kinds to be added,
you need to add rules to the catalog. Rules are added either in a separate
`catalog.rules` key, or added to statically configured locations.
For example, given the following configuration:
```yaml
catalog:
rules:
- allow: [Component, API, System]
locations:
- type: github
target: https://github.com/org/example/blob/master/org-data.yaml
allow: [Group]
```
We are able to add entities of kind `Component`, `API`, or `System` from any
location, and `Group` entities from the `org-data.yaml`, which will also be read
as statically configured location.
Note that if the `catalog.rules` key is present it will replace the default
value, meaning that you need to add rules for `Component` and `API` kinds if you
want those to be allowed.
The following configuration will reject any kind of entities from being added to
the catalog:
```yaml
catalog:
rules: []
```
@@ -12,7 +12,7 @@ Backstage natively supports tracking of the following component
- Documentation
- Other
![](bsc-extend.png)
![](../../assets/software-catalog/bsc-extend.png)
Since these types are likely not the only kind of software you will want to
track in Backstage, it is possible to
@@ -31,9 +31,10 @@ catalog.
It might be tempting to put software that doesn't fit into any of the existing
types into Other. There are a few reasons why we advice against this; firstly,
we have found that it is preferred to match the conceptual model that your
engineers have when describing your sofware. Secondly, Backstage helps your
engineers manage their software by integrating the infratrucure tooling through
plugins. Different plugins are used for managing different types of components.
engineers have when describing your software. Secondly, Backstage helps your
engineers manage their software by integrating the infrastructure tooling
through plugins. Different plugins are used for managing different types of
components.
For example, the
[Lighthouse plugin](https://github.com/spotify/backstage/tree/master/plugins/lighthouse)
+28 -11
View File
@@ -1,6 +1,7 @@
---
id: software-catalog-overview
title: Backstage Service Catalog (alpha)
sidebar_label: Backstage Service Catalog
---
## What is a Service Catalog?
@@ -54,18 +55,18 @@ There are 3 ways to add components to the catalog:
Users can register new components by going to `/create` and clicking the
**REGISTER EXISTING COMPONENT** button:
![](bsc-register-1.png)
![](../../assets/software-catalog/bsc-register-1.png)
Backstage expects the full URL to the YAML in your source control. Example:
```
```bash
https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml
```
_More examples can be found
[here](https://github.com/spotify/backstage/tree/master/packages/catalog-model/examples)._
![](bsc-register-2.png)
![](../../assets/software-catalog/bsc-register-2.png)
It is important to note that any kind of software can be registered in
Backstage. Even if the software is not maintained by your company (SaaS
@@ -78,12 +79,28 @@ All software created through the
[Backstage Software Templates](../software-templates/index.md) are automatically
registered in the catalog.
### Static catalog configuration
In addition to manually registering components, it is also possible to register
components though [static configuration](../../conf/index.md). For example, the
above example can be added using the following configuration:
```yaml
catalog:
locations:
- type: github
target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml
```
More information about catalog configuration can be found
[here](configuration.md).
### Updating component metadata
Teams owning the components are responsible for maintaining the metadata about
them, and do so using their normal Git workflow.
![](bsc-edit.png)
![](../../assets/software-catalog/bsc-edit.png)
Once the change has been merged, Backstage will automatically show the updated
metadata in the service catalog after a short while.
@@ -92,25 +109,25 @@ metadata in the service catalog after a short while.
By default the service catalog shows components owned by the team of the logged
in user. But you can also switch to _All_ to see all the components across your
companie's software ecosystem. Basic inline _search_ and _column filtering_
makes it easy to browse a big set of components.
company's software ecosystem. Basic inline _search_ and _column filtering_ makes
it easy to browse a big set of components.
![](bsc-search.png)
![](../../assets/software-catalog/bsc-search.png)
## Starring components
For easy and quick access to components you visit frequently, Backstage supports
_starring_ of components:
![](bsc-starred.png)
![](../../assets/software-catalog/bsc-starred.png)
## Integrated tooling through plugins
The service catalog is a great way to organise the infrastructure tools you use
The service catalog is a great way to organize the infrastructure tools you use
to manage the software. This is how Backstage creates one developer portal for
all your tools. Rather than asking teams to jump between different
infrastructure UIs (and incurring additional cognitive overhead each time they
make a context switch), most of these tools can be organised around the entities
infrastructure UIs (and incurring additional cognitive overhead each time they
make a context switch), most of these tools can be organized around the entities
in the catalog.
![tools](https://backstage.io/blog/assets/20-05-20/tabs.png)
+26 -22
View File
@@ -16,11 +16,13 @@ We model software in the Backstage catalogue using these three core entities
(further explained below):
- **Components** are individual pieces of software
- **APIs** are the boundaries between different components
- **Resources** are physical or virtual infrastructure needed to operate a
component
![](system-model-core-entities.png)
![](../../assets/software-catalog/software-model-core-entities.png)
### Component
@@ -29,22 +31,22 @@ backend service or data pipeline (list not exhaustive). A component can be
tracked in source control, or use some existing open source or commercial
software.
A component can implement APIs for other components to consume. In turn it
might depend on APIs implemented by other components, or resources that are
attached to it at runtime.
A component can implement APIs for other components to consume. In turn it might
depend on APIs implemented by other components, or resources that are attached
to it at runtime.
### API
APIs form an important (maybe the most important) abstraction that allows large
software ecosystems to scale. Thus, APIs are a first class citizen in the
Backstage model and the primary way to discover existing functionality in
the ecosystem.
Backstage model and the primary way to discover existing functionality in the
ecosystem.
APIs are implemented by components and form boundaries between components. They
might be defined using an RPC IDL (eg Protobuf, GraphQL, ...), a data schema
(eg Avro, TFRecord, ...), or as code interfaces. 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.
might be defined using an RPC IDL (eg Protobuf, GraphQL, ...), a data schema (eg
Avro, TFRecord, ...), or as code interfaces. 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.
APIs have a visibility: they are either public (making them available for any
other component to consume), restricted (only available to a whitelisted set of
@@ -62,12 +64,13 @@ footprint, and create tooling around them.
## Ecosystem Modeling
A large catalogue of components, APIs and resources can be highly granular
and hard to understand as a whole. It might thus be convenient to further
categorize these entities using the following (optional) concepts:
* **Systems** are a collection of entities that cooperate to perform some
function
* **Domains** relate entities and systems to part of the business
A large catalogue of components, APIs and resources can be highly granular and
hard to understand as a whole. It might thus be convenient to further categorize
these entities using the following (optional) concepts:
- **Systems** are a collection of entities that cooperate to perform some
function
- **Domains** relate entities and systems to part of the business
### System
@@ -82,15 +85,16 @@ exposes one or several public APIs. The main benefit of modelling a system is
that it hides its resources and private APIs between the components for any
consumers. This means that as the owner, you can evolve the implementation, in
terms of components and resources, without your consumers being able to notice.
Typically, a system will consist of at most a handful of components (see
Domain for a grouping of systems).
Typically, a system will consist of at most a handful of components (see Domain
for a grouping of systems).
For example, a playlist management system might encapsulate a backend service
to update playlists, a backend service to query them, and a database to store
them. It could expose an RPC API, a daily snapshots dataset, and an event
stream of playlist updates.
For example, a playlist management system might encapsulate a backend service to
update playlists, a backend service to query them, and a database to store them.
It could expose an RPC API, a daily snapshots dataset, and an event stream of
playlist updates.
### Domain
While systems are the basic level of encapsulation for related entities, it is
often useful to group a collection of systems that share terminology, domain
models, metrics, KPIs, business purpose, or documentation, i.e. they form a
@@ -58,7 +58,7 @@ Currently the catalog supports loading definitions from GitHub + Local Files. To
load from other places, not only will there need to be another preparer, but the
support to load the location will also need to be added to the Catalog.
For loading from a file the following command should work when the backend is
For loading from a file, the following command should work when the backend is
running:
```sh
@@ -69,7 +69,7 @@ curl \
--data-raw "{\"type\": \"file\", \"target\": \"${YOUR PATH HERE}/template.yaml\"}"
```
If loading from a git location, you can run the following
If loading from a Git location, you can run the following
```sh
curl \
@@ -83,7 +83,7 @@ This should then have added the catalog, and also should now be listed under the
create page at http://localhost:3000/create.
Alternatively, if you want to get setup with some mock templates that are
already provided for you, you can run the following to load those templates:
already provided, run the following to load those templates:
```
yarn lerna run mock-data
@@ -7,8 +7,8 @@ Publishers are responsible for pushing and storing the templated skeleton after
the values have been templated by the `Templater`. See
[Create your own templater](./create-your-own-templater.md) for more info.
They recieve a directory or location where the templater has sucessfully run on,
and is now ready to store somewhere. They also get given some other options
They receive a directory or location where the templater has sucessfully run
and is now ready to store somewhere. They also are given some other options
which are sent from the frontend, such as the `storePath` which is a string of
where the frontend thinks we should save this templated folder.
@@ -17,7 +17,7 @@ Currently we provide the following `publishers`:
- `github`
This publisher is passed through to the `createRouter` function of the
`@spotify/plugin-scaffolder-backend`. Currently only one publisher is supported,
`@spotify/plugin-scaffolder-backend`. Currently, only one publisher is supported,
but PR's are always welcome.
An full example backend can be found
@@ -8,14 +8,14 @@ returned by the preparers, and then executing the templating command on top of
the file and returning the completed template path. This may or may not be the
same directory as the input directory.
They also recieve additional values from the frontend, which can be used to
They also receive additional values from the frontend, which can be used to
interpolate into the skeleton files.
Currently we provide the following templaters:
- `cookiecutter`
This templater is added the `TemplaterBuilder` and then passed into the
This templater is added to the `TemplaterBuilder` and then passed into the
`createRouter` function of the `@spotify/plugin-scaffolder-backend`
An full example backend can be found
@@ -48,7 +48,7 @@ This `TemplaterKey` is used to select the correct templater from the
`spec.templater` in the
[Template Entity](../../software-catalog/descriptor-format.md#kind-template).
If you wish to add a new templater you'll need to register it with the
If you wish to add a new templater, you'll need to register it with the
`TemplaterBuilder`.
### Creating your own Templater to add to the `TemplaterBuilder`
@@ -83,10 +83,10 @@ follows:
- `dockerClient` - a [dockerode](https://github.com/apocas/dockerode) client to
be able to run docker containers.
_note_ currently the templaters that we provide are basically docker action
_note_ Currently the templaters that we provide are basically Docker action
containers that are run on top of the skeleton folder. This keeps dependencies
to a minimal for running backstage scaffolder, but you don't /have/ to use
docker. You could create your own templater that spins up an EC2 instance and
Docker. You could create your own templater that spins up an EC2 instance and
downloads the folder and does everything using an AMI if you want. It's entirely
up to you!
@@ -138,7 +138,7 @@ spec:
description: Description of the component
```
You see that the `spec.templater` is set as `handlebars`, you'll need to
You see that the `spec.templater` is set as `handlebars`, so you'll need to
register this with the `TemplaterBuilder` like so:
```ts
@@ -5,11 +5,11 @@ title: Extending the Scaffolder
Welcome. Take a seat. You're at the Scaffolder Documentation.
So - You wanna create stuff inside your company from some prebaked templates?
So, you want to create stuff inside your company from some prebaked templates?
You're at the right place.
This guide is gonna take you through how the Scaffolder in Backstage works.
We'll dive into some jargon and run through whats going on in the backend to be
This guide is going to take you through how the Scaffolder in Backstage works.
We'll dive into some jargon and run through what's going on in the backend to be
able to create these templates. There's also more guides that you might find
useful at the bottom of this document. At it's core, theres 3 simple stages.
@@ -25,9 +25,9 @@ scaffolder that you will need to know:
3. Publish
Each of these steps can be configured for your own use case, but we provide some
sensible defaults too.
sensible defaults, too.
Lets dive a little deeper into these phases.
Let's dive a little deeper into these phases.
### Glossary and Jargon
@@ -38,8 +38,8 @@ the router to pick the correct `Preparer` to run for the `Template` entity.
**Templater** - The templater is responsible for actually running the chosen
templater on top of the previously returned temporary directory from the
**Preprarer**. We advise making these docker containers as it can keep all
dependencies, for example Cookiecutter, self contained and not a dependency on
**Preprarer**. We advise making these Docker containers as it can keep all
dependencies--for example Cookiecutter--self contained and not a dependency on
the host machine.
**Publisher** - The publisher is responsible for taking the finished directory,
@@ -50,11 +50,11 @@ passed through to the scaffolder backend.
### How it works
The main of the heavy lifting is done in the
Most of the heavy lifting is done in the
[router.ts](https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/src/service/router.ts#L93)
file in the `scaffolder-backend` plugin.
There are 2 routes defined in the router. `POST /v1/jobs` and
There are two routes defined in the router: `POST /v1/jobs` and
`GET /v1/job/:jobId`
To create a scaffolding job, a JSON object containing the
@@ -78,7 +78,7 @@ additional templating values must be posted as the post body.
The values should represent something that is valid with the `schema` part of
the [Template Entity](../../software-catalog/descriptor-format.md#kind-template)
Once that has been posted, a job will be setup with different stages. And the
Once that has been posted, a job will be setup with different stages, and the
job processor will complete each stage before moving onto the next stage, whilst
collecting logs and mutating the running job.
+6 -6
View File
@@ -4,8 +4,8 @@ title: Software Templates
---
The Software Templates part of Backstage is a tool that can help you create
Components inside Backstage. It by default has the ability to load skeletons of
code, template in some variables and then publish the template to some location
Components inside Backstage. By default, it has the ability to load skeletons of
code, template in some variables, and then publish the template to some location
like GitHub.
<video width="100%" height="100%" controls>
@@ -33,8 +33,8 @@ internally.
![Enter some variables](../../assets/software-templates/template-picked.png)
After filling in these variables, you'll get some more fields to fill out which
are required for backstage usage. The owner, which is a `user` in the backstage
system, and the `storePath` which right now must be a GitHub Organisation and a
are required for backstage usage: the owner, (which is a `user` in the backstage
system), the `storePath` (which right now must be a GitHub Organisation), and a
non-existing github repository name in the format `organisation/reponame`.
![Enter backstage vars](../../assets/software-templates/template-picked-2.png)
@@ -51,13 +51,13 @@ It shouldn't take too long, and you'll have a success screen!
![Templating Complete](../../assets/software-templates/complete.png)
If it fails, you'll be able to click on each section to get the log from the
step that failed which can be helpful to debug.
step that failed which can be helpful in debugging.
![Templating failed](../../assets/software-templates/failed.png)
### View Component in Catalog
When it's been created you'll see the `View in Catalog` button, which will take
When it's been created, you'll see the `View in Catalog` button, which will take
you to the registered component in the catalog:
![Catalog](../../assets/software-templates/go-to-catalog.png)
+4
View File
@@ -101,3 +101,7 @@ more to come...
https://github.com/spotify/backstage/blob/master/packages/techdocs-container
[techdocs/cli]:
https://github.com/spotify/backstage/blob/master/packages/techdocs-cli
## TechDocs Big Picture
![TechDocs Big Picture](../..//assets/techdocs/techdocs_big_picture.png)
+1 -1
View File
@@ -53,4 +53,4 @@ Reader. The reason why transformers were introduced was to provide a way to
transform the HTML content on pre and post render (e.g. rewrite docs links or
modify css).
[Transformers API docs](https://github.com/spotify/backstage/blob/master/plugins/techdocs/src/reader/transformers/README.md)
[Transformers API docs](https://github.com/spotify/backstage/blob/master/plugins/techdocs/src/reader/README.md)
@@ -30,6 +30,11 @@ the documentation template.
Create an entity from the documentation template and you will get the needed
setup for free.
!!! warning Currently the Backstage Software Templates are limited to create repositories
inside GitHub organizations. You also need to generate an personal access token
and use as an environment variable. Read more about this
[here](../software-templates/installation.md#runtime-dependencies).
### Manually add documentation setup to already existing repository
Prerequisities:
@@ -85,7 +85,3 @@ yarn create-plugin # Create a new plugin
> See
> [package.json](https://github.com/spotify/backstage/blob/master/package.json)
> for other yarn commands/options.
[Next Step - Create a Backstage plugin](../plugins/create-a-plugin.md)
[Back to Docs](../README.md)
+15 -2
View File
@@ -3,9 +3,22 @@ id: index
title: Running Backstage Locally
---
First make sure you are using NodeJS with an Active LTS Release, currently v12.
This is made easy with a version manager such as nvm which allows for version switching.
```bash
# Checking your version
node --version
> v14.7.0
# Adding a second node version
nvm install 12
> Downloading and installing node v12.18.3...
> Now using node v12.18.3 (npm v6.14.6)
```
To get up and running with a local Backstage to evaluate it, let's clone it off
of GitHub and run an initial build. First make sure that you have at least node
version 12 installed locally.
of GitHub and run an initial build.
```bash
# Start from your local development folder
+18 -2
View File
@@ -87,14 +87,17 @@ Example of tactics we have used to evangelize Backstage internally:
- Pro-actively identify new plugins. Reach out to teams that own internal UIs or
platforms that you think would make sense to consolidate into Backstage.
## Metrics
## KPIs and metrics
These are some of the metrics that you can use to verify if Backstage has a
successful impact on your software development process:
- **Onboarding time** Time until new engineers are productive. At Spotify we
measure this as the time until the employee has merged their 10th PR (this
metric was down 55% two years after deploying Backstage).
metric was down 55% two years after deploying Backstage). Even though you may
not be onboarding engineers at a rapid pace, this metric is a great proxy for
the overall complexity of your ecosystem. Reducing it will therefore benefit
your whole engineering organization, not just new joiners.
- **Number of merges per developer/day** Less time spent jumping between
different tools and looking for information means more time to focus on
@@ -142,3 +145,16 @@ Backstage as _the_ platform:
- % of contributions coming from outside the central Backstage team (currently
85% inside Spotify)
- Traditional metrics such as visits (MAU, DAU, etc) and page views. Currently
~50% of all Spotifiers use Backstage on a monthly basis, even though the
percentage of engineers is below 50%. Most engineers actually use Backstage on
a daily basis.
Again, any feedback is appreciated. Please use the Edit button at the top of the
page to make a suggestion.
_**Note!** It might be tempting to try to optimize Backstage usage and
"engagement". Even though you want to consolidate all your tooling and technical
documentation in Backstage, it is important to remember that time spent in
Backstage is time not spent writing code_ 🙃
+30
View File
@@ -0,0 +1,30 @@
---
id: background
title: The Spotify Story
---
Backstage was born out of necessity at Spotify. We found that as we grew, our
infrastructure was becoming more fragmented, our engineers less productive.
Instead of building and testing code, teams were spending more time looking for
the right information just to get started. “Wheres the API for that service
were all supposed to be using?” “What version of that framework is everyone
on?” “This service isnt responding, who owns it?” “I cant find documentation
for anything!”
Context switching and cognitive overload were dragging engineers down, day by
day. We needed to make it easier for our engineers to do their work without
having to become an expert in every aspect of infrastructure tooling.
Our idea was to centralize and simplify end-to-end software development with an
abstraction layer that sits on top of all of our infrastructure and developer
tooling. Thats Backstage.
Its a developer portal powered by a centralized service catalog — with a plugin
architecture that makes it endlessly extensible and customizable.
Manage all your services, software, tooling, and testing in Backstage. Start
building a new microservice using an automated template in Backstage. Create,
maintain, and find the documentation for all that software in Backstage.
One place for everything. Accessible to everyone.
+6
View File
@@ -18,12 +18,15 @@ Out of the box, Backstage includes:
- [Backstage Service Catalog](../features/software-catalog/index.md) for
managing all your software (microservices, libraries, data pipelines,
websites, ML models, etc.)
- [Backstage Software Templates](../features/software-templates/index.md) for
quickly spinning up new projects and standardizing your tooling with your
organizations best practices
- [Backstage TechDocs](../features/techdocs/README.md) for making it easy to
create, maintain, find, and use technical documentation, using a "docs like
code" approach
- Plus, a growing ecosystem of
[open source plugins](https://github.com/spotify/backstage/tree/master/plugins)
that further expand Backstages customizability and functionality
@@ -33,12 +36,15 @@ Out of the box, Backstage includes:
- For _engineering managers_, it allows you to maintain standards and best
practices across the organization, and can help you manage your whole tech
ecosystem, from migrations to test certification.
- For _end users_ (developers), it makes it fast and simple to build software
components in a standardized way, and it provides a central place to manage
all projects and documentation.
- For _platform engineers_, it enables extensibility and scalability by letting
you easily integrate new tools and services (via plugins), as well as
extending the functionality of existing ones.
- For _everyone_, its a single, consistent experience that ties all your
infrastructure tooling, resources, standards, owners, contributors, and
administrators together in one place.
+23
View File
@@ -0,0 +1,23 @@
---
id: add-to-marketplace
title: Add to Marketplace
---
## Adding a Plugin to the Marketplace
To add a new plugin to the [plugin marketplace](https://backstage.io/plugins)
create a file in `data/plugins` with your plugin's information. Example:
```yaml
---
title: Your Plugin
author: Your Name
authorUrl: # A link to information about the author E.g. Company url, github user profile, etc
category: Monitoring # A single category e.g. CI, Machine Learning, Services, Monitoring
description: A brief description of the plugin. # Max 170 characters
documentation: # A link to your documentation E.g. Your github README
iconUrl: # Used as the src attribute for your logo.
# You can provide an external url or add your logo under static/img and provide a path
# relative to static/ e.g. img/my-logo.png
npmPackageName: # Your npm package name E.g. '@backstage/plugin-<etc>' quotes are required
```
+3 -8
View File
@@ -71,11 +71,7 @@ Example:
```yaml
# In app-config.yaml
proxy:
'/frobs':
target: 'http://api.frobsco.com/v1'
changeOrigin: true
pathRewrite:
'^/proxy/frobs/': '/'
'/frobs': http://api.frobsco.com/v1
```
```ts
@@ -86,9 +82,8 @@ fetch(`${backendUrl}/proxy/frobs/list`)
.then(payload => setFrobs(payload as Frob[]));
```
The proxy is powered by the `http-proxy-middleware` package, and supports all of
its
[configuration options](https://github.com/chimurai/http-proxy-middleware#options).
The proxy is powered by the `http-proxy-middleware` package. See
[Proxying](proxying.md) for a full description of its configuration options.
Internally at Spotify, the proxy option has been the overwhelmingly most popular
choice for plugin makers. Since we have DNS based service discovery in place and
+5 -13
View File
@@ -15,20 +15,16 @@ dependencies, then run the following on your command line (invoking the
yarn create-plugin
```
<p align='center'>
<img src='https://github.com/spotify/backstage/raw/master/docs/getting-started/create-plugin_output.png' width='600' alt='create plugin'>
</p>
![](../assets/getting-started/create-plugin_output.png)
This will create a new Backstage Plugin based on the ID that was provided. It
will be built and added to the Backstage App automatically.
_If `yarn start` is already running you should be able to see the default page
for your new plugin directly by navigating to
`http://localhost:3000/my-plugin`._
> If `yarn start` is already running you should be able to see the default page
> for your new plugin directly by navigating to
> `http://localhost:3000/my-plugin`.
<p align='center'>
<img src='https://github.com/spotify/backstage/raw/master/docs/plugins/my-plugin_screenshot.png' width='600' alt='my plugin'>
</p>
![](../assets/my-plugin_screenshot.png)
You can also serve the plugin in isolation by running `yarn start` in the plugin
directory. Or by using the yarn workspace command, for example:
@@ -40,7 +36,3 @@ yarn workspace @backstage/plugin-welcome start # Also supports --check
This method of serving the plugin provides quicker iteration speed and a faster
startup and hot reloads. It is only meant for local development, and the setup
for it can be found inside the plugin's `dev/` directory.
[Next Step - Structure of a plugin](structure-of-a-plugin.md)
[Back to Getting Started](../README.md)
+3 -19
View File
@@ -1,6 +1,6 @@
---
id: plugin-development
title: Plugin Development in Backstage
title: Plugin Development
---
Backstage plugins provide features to a Backstage App.
@@ -10,28 +10,12 @@ type of content. Plugins all use a common set of platform APIs and reusable UI
components. Plugins can fetch data from external sources using the regular
browser APIs or by depending on external modules to do the work.
<!-- MOVED TO create-a-plugin.md ## Creating a new plugin
On your command line, invoke the `backstage-cli` to create a new plugin:
```bash
yarn create-plugin
```
![](create-plugin_output.png)
This will create a new Backstage Plugin based on the ID that was provided. It will be built and
added to the Backstage App automatically.
*If `yarn start` is already running you should be able to see the default page for your new
plugin directly by navigating to `http://localhost:3000/my-plugin`.*
![](my-plugin_screenshot.png) -->
## Developing guidelines
- Consider writing plugins in `TypeScript`.
- Plan the directory structure of your plugin so that it becomes easy to manage.
- Prefer using the Backstage components, otherwise go with
[Material-UI](https://material-ui.com/).
- Prefer using the [Backstage components](https://backstage.io/storybook),
otherwise go with [Material-UI](https://material-ui.com/).
- Check out the shared Backstage APIs before building a new one.
## Plugin concepts / API
+70 -1
View File
@@ -3,4 +3,73 @@ id: proxying
title: Proxying
---
## TODO
## Overview
The Backstage backend comes packaged with a basic HTTP proxy, that can aid in
reaching backend service APIs from frontend plugin code. See
[Call Existing API](call-existing-api.md) for a description of when the proxy
can be the best choice for communicating with an API.
## Getting Started
The plugin is already added to a default Backstage project.
In `packages/backend/src/index.ts`:
```ts
const proxyEnv = useHotMemoize(module, () => createEnv('proxy'));
const service = createServiceBuilder(module)
.loadConfig(configReader)
/** ... other routers ... */
.addRouter('/proxy', await proxy(proxyEnv, '/proxy'));
```
## Configuration
Configuration for the proxy plugin lives under a `proxy` root key of your
`app-config.yaml` file.
Example:
```yaml
# in app-config.yaml
proxy:
'/simple-example': http://simple.example.com:8080
'/larger-example/v1':
target: http://larger.example.com:8080/svc.v1
headers:
Authorization:
$secret:
env: EXAMPLE_AUTH_HEADER
```
Each key under the proxy configuration entry is a route to match, below the
prefix that the proxy plugin is mounted on. It must start with a slash. For
example, if the backend mounts the proxy plugin as `/proxy`, the above
configuration will lead to the proxy acting on backend requests to
`/proxy/simple-example/...` and `/proxy/larger-example/v1/...`.
The value inside each route is either a simple URL string, or an object on the
format accepted by
[http-proxy-middleware](https://www.npmjs.com/package/http-proxy-middleware).
If the value is a string, it is assumed to correspond to:
```yaml
target: <the string>
changeOrigin: true
pathRewrite:
'^<url prefix><the string>/': '/'
```
When the target is an object, it is given verbatim to `http-proxy-middleware`
except with the following caveats for convenience:
- If `changeOrigin` is not specified, it is set to `true`. This is the most
commonly useful value.
- If `pathRewrite` is not specified, it is set to a single rewrite that removes
the entire prefix and route. In the above example, a rewrite of
`'^/proxy/larger-example/v1/': '/'` is added. That means that a request to
`/proxy/larger-example/v1/some/path` will be translated to a request to
`http://larger.example.com:8080/svc.v1/some/path`.
-2
View File
@@ -37,5 +37,3 @@ const myPluginRouteRef = createRouteRef({
title: 'My Plugin',
});
```
[Back to References](../README.md)
-6
View File
@@ -1,6 +0,0 @@
---
id: index
title: Overview
---
## Coming soon!
+11 -8
View File
@@ -1,12 +1,15 @@
# Purpose
---
id: journey
title: Future developer journey
---
This RFC describes a possible journey of a future Backstage plugin developer as
they build a plugin that touches many different aspects of a Backstage. The
story invents many new things that are not part of Backstage today, but are
things that I'm suggesting we should add as long term or north star goals. The
idea is to discuss what parts of the story makes sense to aim for, and what we'd
want to do differently or not at all. The "chapters" are numbered to make it a
bit easier to comment on parts of the story.
> This document describes a possible journey of a **_future_** Backstage plugin
> developer as they build a plugin that touches many different aspects of a
> Backstage. The story invents many new things that are not part of Backstage
> today, but are things that I'm suggesting we should add as long term or north
> star goals. The idea is to discuss what parts of the story makes sense to aim
> for, and what we'd want to do differently or not at all. The "chapters" are
> numbered to make it a bit easier to comment on parts of the story.
# The Protagonist
+2 -2
View File
@@ -23,7 +23,7 @@
| app.resources | Kubernetes Pod resource requests/limits | `{}` |
| app.nodeSelector | Node selectors for scheduling app/frontend pods | `{}` |
| app.tolerations | Tolerations for scheduling app/frontend pods | `{}` |
| app.affinity | Affinity setttings for scheduling app/frontend pods | `{}` |
| app.affinity | Affinity settings for scheduling app/frontend pods | `{}` |
## Backend Values
@@ -48,4 +48,4 @@
| backend.resources | Kubernetes Pod resource requests/limits | `{}` |
| backend.nodeSelector | Node selectors for scheduling backend pods | `{}` |
| backend.tolerations | Tolerations for scheduling backend pods | `{}` |
| backend.affinity | Affinity setttings for scheduling backend pods | `{}` |
| backend.affinity | Affinity settings for scheduling backend pods | `{}` |
+1 -1
View File
@@ -2,5 +2,5 @@
"packages": ["packages/*", "plugins/*"],
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.1.1-alpha.18"
"version": "0.1.1-alpha.20"
}
+9
View File
@@ -0,0 +1,9 @@
---
title: CircleCI
author: Spotify
authorUrl: https://www.spotify.com/
category: CI
description: Automate your development process with CI hosted in the cloud or on a private server.
documentation: https://github.com/spotify/backstage/tree/master/plugins/circleci
iconUrl: https://d3r49iyjzglexf.cloudfront.net/logo-wordmark-26f8eaea9b0f6e13b90d3f4a8fd8fda31490f5af41daab98bbede45037682576.svg
npmPackageName: '@backstage/plugin-circleci'
@@ -0,0 +1,14 @@
---
title: GitOps Clusters
author: Weaveworks
authorUrl: https://www.weave.works/
category: Kubernetes
description: Create GitOps-managed Kubernetes clusters. Currently, it supports provisioning EKS clusters on GitHub via GitHub Actions.
documentation: https://github.com/spotify/backstage/tree/master/plugins/gitops-profiles
iconUrl: https://res-5.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_256,w_256,f_auto,q_auto:eco/v1462316670/i9d3delzvx1erzjhmcws.png
npmPackageName: '@backstage/plugin-gitops-profiles'
tags:
- kubernetes
- gitops
- github
- eks
+13
View File
@@ -0,0 +1,13 @@
---
title: GraphiQL
author: Spotify
authorUrl: https://www.spotify.com/
category: Debugging
description: Integrates GraphiQL as a tool to browse GraphiQL endpoints inside Backstage.
documentation: https://github.com/spotify/backstage/tree/master/plugins/lighthouse
iconUrl: https://upload.wikimedia.org/wikipedia/commons/thumb/1/17/GraphQL_Logo.svg/1024px-GraphQL_Logo.svg.png
npmPackageName: '@backstage/plugin-graphiql'
tags:
- graphql
- github
- gitlab
+14
View File
@@ -0,0 +1,14 @@
---
title: Lighthouse
author: Spotify
authorUrl: https://www.spotify.com/
category: Accessibility
description: Google's Lighthouse tool is a great resource for benchmarking and improving the accessibility, performance, SEO, and best practices of your website.
documentation: https://github.com/spotify/backstage/tree/master/plugins/lighthouse
iconUrl: https://seeklogo.com/images/G/google-lighthouse-logo-1C7FA08580-seeklogo.com.png
npmPackageName: '@backstage/plugin-lighthouse'
tags:
- web
- seo
- accessibility
- performance
+14
View File
@@ -0,0 +1,14 @@
---
title: New Relic
author: '@timwheelercom'
authorUrl: https://github.com/timwheelercom
category: Monitoring
description: Observability platform built to help engineers create and monitor their software.
documentation: https://github.com/spotify/backstage/tree/master/plugins/newrelic
iconUrl: https://www.mulesoft.com/sites/default/files/2018-10/New_relic.png
npmPackageName: '@backstage/plugin-newrelic'
tags:
- performance
- monitoring
- errors
- alerting
+10
View File
@@ -0,0 +1,10 @@
---
title: Rollbar
author: '@andrewthauer'
authorUrl: https://github.com/andrewthauer
category: Monitoring
description: View Rollbar errors for your services in Backstage.
documentation: https://github.com/spotify/backstage/tree/master/plugins/rollbar
iconUrl: https://rollbar.com/assets/media/rollbar-mark-color.png
npmPackageName: '@backstage/plugin-rollbar'
+10
View File
@@ -0,0 +1,10 @@
---
title: Sentry
author: Spotify
authorUrl: https://www.spotify.com/
category: Monitoring
description: View Sentry issues in Backstage.
documentation: https://github.com/spotify/backstage/tree/master/plugins/sentry
iconUrl: https://sentry-brand.storage.googleapis.com/sentry-glyph-white.png
npmPackageName: '@backstage/plugin-sentry'
+9
View File
@@ -0,0 +1,9 @@
---
title: Tech Radar
author: Spotify
authorUrl: https://www.spotify.com/
category: Discovery
description: Visualize the your company's official guidelines of different areas of software development.
documentation: https://github.com/spotify/backstage/tree/master/plugins/tech-radar
iconUrl: https://github.com/spotify/backstage/raw/master/plugins/tech-radar/docs/screenshot.png
npmPackageName: '@backstage/plugin-tech-radar'
+10
View File
@@ -0,0 +1,10 @@
---
title: Travis CI
author: roadie.io
authorUrl: https://roadie.io/
category: CI
description: View Travis CI builds for your service in Backstage.
documentation: https://roadie.io/backstage/plugins/travis-ci
iconUrl: https://roadie.io/static/af2941eaf0af675facb281d566f42e14/45f2b/travis-ci-mascot-200x200.png
npmPackageName: '@roadiehq/backstage-plugin-travis-ci'
+13 -9
View File
@@ -51,7 +51,7 @@
"title": "Adding authentication providers"
},
"auth/auth-backend-classes": {
"title": "auth/auth-backend-classes"
"title": "Auth backend classes"
},
"auth/auth-backend": {
"title": "Auth backend"
@@ -103,7 +103,8 @@
"title": "External integrations"
},
"features/software-catalog/software-catalog-overview": {
"title": "Backstage Service Catalog (alpha)"
"title": "Backstage Service Catalog (alpha)",
"sidebar_label": "Backstage Service Catalog"
},
"features/software-catalog/installation": {
"title": "features/software-catalog/installation"
@@ -174,9 +175,6 @@
"getting-started/installation": {
"title": "Installation"
},
"journey": {
"title": "journey"
},
"overview/adopting": {
"title": "Strategies for adopting"
},
@@ -186,6 +184,9 @@
"overview/architecture-terminology": {
"title": "Architecture terminology"
},
"overview/background": {
"title": "The Spotify Story"
},
"overview/roadmap": {
"title": "Project roadmap"
},
@@ -198,6 +199,9 @@
"overview/what-is-backstage": {
"title": "What is Backstage?"
},
"plugins/add-to-marketplace": {
"title": "Add to Marketplace"
},
"plugins/backend-plugin": {
"title": "Backend plugin"
},
@@ -214,7 +218,7 @@
"title": "Intro to plugins"
},
"plugins/plugin-development": {
"title": "Plugin Development in Backstage"
"title": "Plugin Development"
},
"plugins/proxying": {
"title": "Proxying"
@@ -285,16 +289,16 @@
"reference/utility-apis/StorageApi": {
"title": "reference/utility-apis/StorageApi"
},
"tutorials/index": {
"title": "Overview"
"tutorials/journey": {
"title": "Future developer journey"
}
},
"links": {
"GitHub": "GitHub",
"Docs": "Docs",
"Plugins": "Plugins",
"Blog": "Blog",
"Demos": "Demos",
"The Spotify story": "The Spotify story",
"Newsletter": "Newsletter"
},
"categories": {
+3 -2
View File
@@ -1,5 +1,5 @@
{
"version": "0.1.1-alpha.19",
"version": "0.1.1-alpha.20",
"name": "backstage-microsite",
"license": "Apache-2.0",
"private": true,
@@ -13,6 +13,7 @@
"rename-version": "docusaurus-rename-version"
},
"devDependencies": {
"docusaurus": "^2.0.0-alpha.61"
"docusaurus": "^2.0.0-alpha.61",
"js-yaml": "^3.14.0"
}
}
-122
View File
@@ -1,122 +0,0 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require('react');
const Components = require(`${process.cwd()}/core/Components.js`);
const Block = Components.Block;
const Breakpoint = Components.Breakpoint;
const Background = props => {
const { config: siteConfig } = props;
const { baseUrl } = siteConfig;
return (
<div className="mainWrapper">
<Block>
<Block.Container column>
<Block.TitleBox story>The Spotify Story</Block.TitleBox>
<Block.TextBox>
<Block.Paragraph>
Backstage was born out of necessity at Spotify. We found that as
we grew, our infrastructure was becoming more fragmented, our
engineers less productive.{' '}
</Block.Paragraph>
<Block.Paragraph>
Instead of building and testing code, teams were spending more
time looking for the right information just to get started.
Wheres the API for that service were all supposed to be using?
What version of that framework is everyone on? This service
isnt responding, who owns it? I cant find documentation for
anything!{' '}
</Block.Paragraph>
<Breakpoint
narrow={
<Block small>
<Block.QuoteContainer>
<Block.Divider quote />
<Block.Quote>
One place for everything. Accessible to everyone.
</Block.Quote>
</Block.QuoteContainer>
</Block>
}
></Breakpoint>
<Block.Paragraph>
Context switching and cognitive overload were dragging engineers
down, day by day. We needed to make it easier for our engineers to
do their work without having to become an expert in every aspect
of infrastructure tooling.
</Block.Paragraph>
<Block.Paragraph>
Our idea was to centralize and simplify end-to-end software
development with an abstraction layer that sits on top of all of
our infrastructure and developer tooling. Thats Backstage.
</Block.Paragraph>
<Block.Paragraph>
Its a developer portal powered by a centralized service catalog
with a plugin architecture that makes it endlessly extensible and
customizable.
</Block.Paragraph>
<Block.Paragraph>
Manage all your services, software, tooling, and testing in
Backstage. Start building a new microservice using an automated
template in Backstage. Create, maintain, and find the
documentation for all that software in Backstage.{' '}
</Block.Paragraph>
<Block.Paragraph>
One place for everything. Accessible to everyone.
</Block.Paragraph>
<Breakpoint
narrow={
<Block small>
<Block.LinkButton stretch href={'https://backstage.io/'}>
Explore Features
</Block.LinkButton>
</Block>
}
></Breakpoint>
</Block.TextBox>
</Block.Container>
</Block>
<Breakpoint
wide={
<Block small>
<Block.QuoteContainer>
<Block.Divider quote />
<Block.Quote>
One place for everything. Accessible to everyone.
</Block.Quote>
<Block.LinkButton stretch href={'https://backstage.io/'}>
Explore Features
</Block.LinkButton>
</Block.QuoteContainer>
</Block>
}
></Breakpoint>
<div
style={{
zIndex: -1,
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundImage: `linear-gradient( to bottom, rgb(18, 18, 18), rgba(0, 0, 0, 0) ), url(../img/dot.svg)`,
}}
/>
</div>
);
};
module.exports = Background;
+84
View File
@@ -0,0 +1,84 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const fs = require('fs');
const yaml = require('js-yaml');
const React = require('react');
const Components = require(`${process.cwd()}/core/Components.js`);
const {
Block: { Container },
BulletLine,
} = Components;
const pluginsDirectory = require('path').join(process.cwd(), 'data/plugins');
const pluginMetadata = fs
.readdirSync(pluginsDirectory)
.map(file =>
yaml.safeLoad(fs.readFileSync(`./data/plugins/${file}`, 'utf8')),
);
const truncate = text =>
text.length > 170 ? text.substr(0, 170) + '...' : text;
const addPluginDocsLink = '/docs/plugins/add-to-marketplace';
const defaultIconUrl = 'img/logo-gradient-on-dark.svg';
const Plugins = () => (
<main className="MainContent">
<div className="PluginPageLayout">
<div className="PluginPageHeader">
<h2>Plugins</h2>
<span>
<a
className="PluginAddNewButton ButtonFilled"
href={addPluginDocsLink}
>
<b>Add Plugin</b>
</a>
</span>
</div>
<BulletLine style={{ width: '100% ' }} />
<Container wrapped className="grid">
{pluginMetadata.map(
({
iconUrl,
title,
description,
author,
authorUrl,
documentation,
category,
}) => (
<div className="PluginCard">
<div className="PluginCardHeader">
<img src={iconUrl || defaultIconUrl} alt={title} />
<h2 className="PluginCardTitle">{title}</h2>
<p className="Author">
by <a href={authorUrl}>{author}</a>
</p>
<span className="ChipOutlined">{category}</span>
</div>
<div className="PluginCardBody">
<p>{truncate(description)}</p>
</div>
<Container className="PluginCardFooter">
<span>
<a
className="PluginCardLink ButtonFilled"
href={documentation}
>
docs
</a>
</span>
</Container>
</div>
),
)}
</Container>
</div>
</main>
);
module.exports = Plugins;
+3 -3
View File
@@ -6,6 +6,7 @@
"overview/architecture-terminology",
"overview/roadmap",
"overview/vision",
"overview/background",
"overview/adopting"
],
"Getting Started": [
@@ -38,7 +39,6 @@
"features/software-catalog/software-catalog-overview",
"features/software-catalog/system-model",
"features/software-catalog/descriptor-format",
"features/software-catalog/populating-catalog",
"features/software-catalog/extending-the-model",
"features/software-catalog/external-integrations",
"features/software-catalog/software-catalog-api"
@@ -91,7 +91,7 @@
{
"type": "subcategory",
"label": "Publishing",
"ids": ["plugins/publishing", "plugins/publish-private"]
"ids": ["plugins/publishing", "plugins/publish-private", "plugins/add-to-marketplace"]
}
],
"Configuration": [
@@ -132,7 +132,7 @@
"ids": ["api/backend"]
}
],
"Tutorials": ["tutorials/index"],
"Tutorials": ["tutorials/journey"],
"Architecture Decision Records (ADRs)": [
"architecture-decisions/adrs-overview",
"architecture-decisions/adrs-adr001",
+11 -10
View File
@@ -17,17 +17,12 @@ const siteConfig = {
url: 'https://backstage.io', // Your website URL
cname: 'backstage.io',
baseUrl: '/', // Base URL for your project */
// For github.io type URLs, you would set the url and baseUrl like:
// url: 'https://facebook.github.io',
// baseUrl: '/test-site/',
editUrl: 'https://github.com/spotify/backstage/edit/master/docs/',
// Used for publishing and more
projectName: 'backstage',
organizationName: 'Spotify',
fossWebsite: 'https://spotify.github.io/',
// For top-level user or org sites, the organization is still the same.
// e.g., for the https://JoelMarcey.github.io site, it would be set like...
// organizationName: 'JoelMarcey'
// Google Analytics
gaTrackingId: 'UA-48912878-10',
@@ -43,6 +38,10 @@ const siteConfig = {
href: '/docs',
label: 'Docs',
},
{
page: 'plugins',
label: 'Plugins',
},
{
page: 'blog',
blog: true,
@@ -52,10 +51,6 @@ const siteConfig = {
page: 'demos',
label: 'Demos',
},
{
page: 'background',
label: 'The Spotify story',
},
{
href: 'https://mailchi.mp/spotify/backstage-community',
label: 'Newsletter',
@@ -119,6 +114,12 @@ const siteConfig = {
stylesheets: [
'https://fonts.googleapis.com/css?family=IBM+Plex+Mono:500,700&display=swap',
],
algolia: {
apiKey: '8d115c9875ba0f4feaee95bab55a1645',
indexName: 'backstage',
searchParameters: {}, // Optional (if provided by Algolia)
},
};
module.exports = siteConfig;
+2 -2
View File
@@ -58,7 +58,7 @@ ol,
li,
td {
font-family: Helvetica Neue, sans-serif;
color: $textColor;
color: #e4e4e4;
}
.mainWrapper {
@@ -216,7 +216,7 @@ td {
code {
font-family: IBM Plex Mono, Menlo, Monaco, Consolas, Courier New, monospace;
font-weight: 500;
background-color: #0e0e0e;
background-color: #272822;
}
/* .stripe {
+111
View File
@@ -0,0 +1,111 @@
.PluginCard {
background-color: #272822;
height: 100%;
padding: 16px;
display: flex;
flex-direction: column;
}
.grid {
display: grid;
grid-gap: 1rem;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 1fr;
padding-top: 32px;
}
@media (max-width: 1200px) {
.grid {
grid-template-columns: repeat(3, 1fr);
}
}
@media only screen and (max-width: 815px) {
.grid {
grid-template-columns: repeat(2, 1fr);
}
}
.PluginCard img {
float: left;
margin: 0px 16px 8px 0px;
height: 100px;
width: 100px;
}
.PluginCardHeader {
max-height: fit-content;
min-height: fit-content;
}
.PluginCardTitle {
color: white;
vertical-align: top;
margin: 8px 0px 0px 16px;
}
.PluginAddNewButton {
position: absolute;
bottom: 16px;
right: 0px;
}
.ButtonFilled {
padding: 4px 8px;
border-radius: 4px;
background-color: #36BAA2;
color: white;
margin-top: 36px;
}
.ButtonFilled:hover {
border: 1px solid #36BAA2;
background-color: transparent;
}
.ChipOutlined {
font-size: small;
border-radius: 16px;
padding: 2px 8px;
border: 1px solid #36BAA2;
color: #36BAA2;
}
.PluginCardLink {
padding: 2px 8px;
position: absolute;
bottom: 0;
right: 0;
}
.PluginPageLayout {
margin: auto;
max-width: 1430px;
padding: 20px;
}
.PluginPageHeader {
position: relative;
}
.PluginPageHeader h2 {
display: inline-block;
}
.PluginCardBody {
padding-top: 8px;
}
.PluginCardFooter {
position: relative;
min-height: 2em;
}
.Author, .Author a {
margin-bottom: 0.25em;
color: rgba(255,255,255, 0.6);
}
.Author a:hover {
color: white;
}
+2
View File
@@ -8,6 +8,7 @@ nav:
- Architecture and terminology: 'overview/architecture-terminology.md'
- Roadmap: 'overview/roadmap.md'
- Vision: 'overview/vision.md'
- The Spotify story: 'overview/background.md'
- Strategies for adopting: 'overview/adopting.md'
- Getting started:
- Running Backstage locally: 'getting-started/index.md'
@@ -27,6 +28,7 @@ nav:
- Overview: 'features/software-catalog/index.md'
- System model: 'features/software-catalog/system-model.md'
- YAML File Format: 'features/software-catalog/descriptor-format.md'
- Configuration: 'features/software-catalog/configuration.md'
- Extending the model: 'features/software-catalog/extending-the-model.md'
- External integrations: 'features/software-catalog/external-integrations.md'
- API: 'features/software-catalog/api.md'
+5
View File
@@ -60,5 +60,10 @@
"*.{json,md}": [
"prettier --write"
]
},
"jest": {
"transformModules": [
"@kyma-project/asyncapi-react"
]
}
}
+22 -22
View File
@@ -1,29 +1,29 @@
{
"name": "example-app",
"version": "0.1.1-alpha.19",
"version": "0.1.1-alpha.20",
"private": true,
"dependencies": {
"@backstage/cli": "^0.1.1-alpha.19",
"@backstage/core": "^0.1.1-alpha.19",
"@backstage/plugin-api-docs": "^0.1.1-alpha.19",
"@backstage/plugin-catalog": "^0.1.1-alpha.19",
"@backstage/plugin-circleci": "^0.1.1-alpha.19",
"@backstage/plugin-explore": "^0.1.1-alpha.19",
"@backstage/plugin-github-actions": "^0.1.1-alpha.19",
"@backstage/plugin-gitops-profiles": "^0.1.1-alpha.19",
"@backstage/plugin-graphiql": "^0.1.1-alpha.19",
"@backstage/plugin-jenkins": "^0.1.1-alpha.19",
"@backstage/plugin-lighthouse": "^0.1.1-alpha.19",
"@backstage/plugin-newrelic": "^0.1.1-alpha.19",
"@backstage/plugin-register-component": "^0.1.1-alpha.19",
"@backstage/plugin-rollbar": "^0.1.1-alpha.19",
"@backstage/plugin-scaffolder": "^0.1.1-alpha.19",
"@backstage/plugin-sentry": "^0.1.1-alpha.19",
"@backstage/plugin-tech-radar": "^0.1.1-alpha.19",
"@backstage/plugin-techdocs": "^0.1.1-alpha.19",
"@backstage/plugin-welcome": "^0.1.1-alpha.19",
"@backstage/test-utils": "^0.1.1-alpha.19",
"@backstage/theme": "^0.1.1-alpha.19",
"@backstage/cli": "^0.1.1-alpha.20",
"@backstage/core": "^0.1.1-alpha.20",
"@backstage/plugin-api-docs": "^0.1.1-alpha.20",
"@backstage/plugin-catalog": "^0.1.1-alpha.20",
"@backstage/plugin-circleci": "^0.1.1-alpha.20",
"@backstage/plugin-explore": "^0.1.1-alpha.20",
"@backstage/plugin-github-actions": "^0.1.1-alpha.20",
"@backstage/plugin-gitops-profiles": "^0.1.1-alpha.20",
"@backstage/plugin-graphiql": "^0.1.1-alpha.20",
"@backstage/plugin-jenkins": "^0.1.1-alpha.20",
"@backstage/plugin-lighthouse": "^0.1.1-alpha.20",
"@backstage/plugin-newrelic": "^0.1.1-alpha.20",
"@backstage/plugin-register-component": "^0.1.1-alpha.20",
"@backstage/plugin-rollbar": "^0.1.1-alpha.20",
"@backstage/plugin-scaffolder": "^0.1.1-alpha.20",
"@backstage/plugin-sentry": "^0.1.1-alpha.20",
"@backstage/plugin-tech-radar": "^0.1.1-alpha.20",
"@backstage/plugin-techdocs": "^0.1.1-alpha.20",
"@backstage/plugin-welcome": "^0.1.1-alpha.20",
"@backstage/test-utils": "^0.1.1-alpha.20",
"@backstage/theme": "^0.1.1-alpha.20",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@octokit/rest": "^18.0.0",
+3
View File
@@ -27,6 +27,9 @@ describe('App', () => {
data: {
app: { title: 'Test' },
backend: { baseUrl: 'http://localhost:7000' },
lighthouse: {
baseUrl: 'http://localhost:3003',
},
techdocs: {
storageUrl: 'http://localhost:7000/techdocs/static/docs',
},
+17 -36
View File
@@ -24,6 +24,8 @@ import {
ErrorAlerter,
featureFlagsApiRef,
FeatureFlags,
discoveryApiRef,
UrlPatternDiscovery,
GoogleAuth,
GithubAuth,
OAuth2,
@@ -90,6 +92,10 @@ export const apis = (config: ConfigApi) => {
const builder = ApiRegistry.builder();
const discoveryApi = builder.add(
discoveryApiRef,
UrlPatternDiscovery.compile(`${backendUrl}/{{ pluginId }}`),
);
const alertApi = builder.add(alertApiRef, new AlertApiForwarder());
const errorApi = builder.add(
errorApiRef,
@@ -108,7 +114,7 @@ export const apis = (config: ConfigApi) => {
builder.add(featureFlagsApiRef, new FeatureFlags());
builder.add(lighthouseApiRef, new LighthouseRestApi('http://localhost:3003'));
builder.add(lighthouseApiRef, LighthouseRestApi.fromConfig(config));
builder.add(travisCIApiRef, new TravisCIApi());
builder.add(githubPullRequestsApiRef, new GithubPullRequestsClient());
@@ -121,8 +127,7 @@ export const apis = (config: ConfigApi) => {
builder.add(
googleAuthApiRef,
GoogleAuth.create({
backendUrl,
basePath: '/auth/',
discoveryApi,
oauthRequestApi,
}),
);
@@ -130,8 +135,7 @@ export const apis = (config: ConfigApi) => {
builder.add(
microsoftAuthApiRef,
MicrosoftAuth.create({
backendUrl,
basePath: '/auth/',
discoveryApi,
oauthRequestApi,
}),
);
@@ -139,8 +143,7 @@ export const apis = (config: ConfigApi) => {
const githubAuthApi = builder.add(
githubAuthApiRef,
GithubAuth.create({
backendUrl,
basePath: '/auth/',
discoveryApi,
oauthRequestApi,
}),
);
@@ -148,8 +151,7 @@ export const apis = (config: ConfigApi) => {
builder.add(
oktaAuthApiRef,
OktaAuth.create({
backendUrl,
basePath: '/auth/',
discoveryApi,
oauthRequestApi,
}),
);
@@ -157,8 +159,7 @@ export const apis = (config: ConfigApi) => {
builder.add(
gitlabAuthApiRef,
GitlabAuth.create({
backendUrl,
basePath: '/auth/',
discoveryApi,
oauthRequestApi,
}),
);
@@ -166,8 +167,7 @@ export const apis = (config: ConfigApi) => {
builder.add(
auth0AuthApiRef,
Auth0Auth.create({
backendUrl,
basePath: '/auth/',
discoveryApi,
oauthRequestApi,
}),
);
@@ -175,8 +175,7 @@ export const apis = (config: ConfigApi) => {
builder.add(
oauth2ApiRef,
OAuth2.create({
backendUrl,
basePath: '/auth/',
discoveryApi,
oauthRequestApi,
}),
);
@@ -189,21 +188,9 @@ export const apis = (config: ConfigApi) => {
}),
);
builder.add(
catalogApiRef,
new CatalogClient({
apiOrigin: backendUrl,
basePath: '/catalog',
}),
);
builder.add(catalogApiRef, new CatalogClient({ discoveryApi }));
builder.add(
scaffolderApiRef,
new ScaffolderApi({
apiOrigin: backendUrl,
basePath: '/scaffolder/v1',
}),
);
builder.add(scaffolderApiRef, new ScaffolderApi({ discoveryApi }));
builder.add(gitOpsApiRef, new GitOpsRestApi('http://localhost:3008'));
@@ -224,13 +211,7 @@ export const apis = (config: ConfigApi) => {
]),
);
builder.add(
rollbarApiRef,
new RollbarClient({
apiOrigin: backendUrl,
basePath: '/rollbar',
}),
);
builder.add(rollbarApiRef, new RollbarClient({ discoveryApi }));
builder.add(
techdocsStorageApiRef,
+2 -1
View File
@@ -39,6 +39,7 @@ import {
SidebarUserSettings,
SidebarThemeToggle,
SidebarPinButton,
DefaultProviderSettings,
} from '@backstage/core';
import { NavLink } from 'react-router-dom';
import { graphiQLRouteRef } from '@backstage/plugin-graphiql';
@@ -107,7 +108,7 @@ const Root: FC<{}> = ({ children }) => (
<SidebarSpace />
<SidebarDivider />
<SidebarThemeToggle />
<SidebarUserSettings />
<SidebarUserSettings providerSettings={<DefaultProviderSettings />} />
<SidebarPinButton />
</Sidebar>
{children}
+9 -7
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-common",
"description": "Common functionality library for Backstage backends",
"version": "0.1.1-alpha.19",
"version": "0.1.1-alpha.20",
"main": "src/index.ts",
"types": "src/index.ts",
"private": false,
@@ -29,22 +29,24 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/cli-common": "^0.1.1-alpha.19",
"@backstage/config": "^0.1.1-alpha.19",
"@backstage/config-loader": "^0.1.1-alpha.19",
"@backstage/cli-common": "^0.1.1-alpha.20",
"@backstage/config": "^0.1.1-alpha.20",
"@backstage/config-loader": "^0.1.1-alpha.20",
"@types/cors": "^2.8.6",
"@types/express": "^4.17.6",
"compression": "^1.7.4",
"cors": "^2.8.5",
"express": "^4.17.1",
"express-prom-bundle": "^6.1.0",
"express-promise-router": "^3.0.3",
"helmet": "^4.0.0",
"knex": "^0.21.1",
"lodash": "^4.17.15",
"morgan": "^1.10.0",
"prom-client": "^12.0.0",
"selfsigned": "^1.10.7",
"stoppable": "^1.1.0",
"winston": "^3.2.1",
"selfsigned": "^1.10.7"
"winston": "^3.2.1"
},
"peerDependencies": {
"pg-connection-string": "^2.3.0"
@@ -55,7 +57,7 @@
}
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.19",
"@backstage/cli": "^0.1.1-alpha.20",
"@types/compression": "^1.7.0",
"@types/http-errors": "^1.6.3",
"@types/morgan": "^1.9.0",
@@ -37,6 +37,7 @@ import {
HttpsSettings,
} from './config';
import { createHttpServer, createHttpsServer } from './hostFactory';
import { metricsHandler } from './metrics';
const DEFAULT_PORT = 7000;
// '' is express default, which listens to all interfaces
@@ -48,6 +49,7 @@ export class ServiceBuilderImpl implements ServiceBuilder {
private logger: Logger | undefined;
private corsOptions: cors.CorsOptions | undefined;
private httpsSettings: HttpsSettings | undefined;
private enableMetrics: boolean = true;
private routers: [string, Router][];
// Reference to the module where builder is created - needed for hot module
// reloading
@@ -82,6 +84,9 @@ export class ServiceBuilderImpl implements ServiceBuilder {
this.httpsSettings = httpsSettings;
}
// For now, configuration of metrics is a simple boolean and active by default
this.enableMetrics = backendConfig.getOptionalBoolean('metrics') !== false;
return this;
}
@@ -131,6 +136,9 @@ export class ServiceBuilderImpl implements ServiceBuilder {
}
app.use(compression());
app.use(express.json());
if (this.enableMetrics) {
app.use(metricsHandler());
}
app.use(requestLoggingHandler());
for (const [root, route] of this.routers) {
app.use(root, route);
@@ -0,0 +1,37 @@
/*
* 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 prom from 'prom-client';
import promBundle from 'express-prom-bundle';
import { RequestHandler } from 'express';
/**
* Adds a /metrics endpoint, register default runtime metrics and instrument the router.
*/
export function metricsHandler(): RequestHandler {
// We can only initialize the metrics once and have to clean them up between hot reloads
prom.register.clear();
return promBundle({
includeMethod: true,
includePath: true,
// Using includePath alone is problematic, as it will include path labels with high
// cardinality (e.g. path params). Instead we would have to template them. However, this
// is difficult, as every backend plugin might use different routes. Instead we only take
// the first directory of the path, to have at least an idea how each plugin performs:
normalizePath: [['^/([^/]*)/.*', '/$1']],
promClient: { collectDefaultMetrics: {} },
});
}
+16 -14
View File
@@ -1,6 +1,6 @@
{
"name": "example-backend",
"version": "0.1.1-alpha.19",
"version": "0.1.1-alpha.20",
"main": "dist/index.cjs.js",
"types": "src/index.ts",
"private": true,
@@ -18,18 +18,20 @@
"migrate:create": "knex migrate:make -x ts"
},
"dependencies": {
"@backstage/backend-common": "^0.1.1-alpha.19",
"@backstage/catalog-model": "^0.1.1-alpha.19",
"@backstage/config": "^0.1.1-alpha.19",
"@backstage/plugin-auth-backend": "^0.1.1-alpha.19",
"@backstage/plugin-catalog-backend": "^0.1.1-alpha.19",
"@backstage/plugin-graphql-backend": "^0.1.1-alpha.19",
"@backstage/plugin-identity-backend": "^0.1.1-alpha.19",
"@backstage/plugin-proxy-backend": "^0.1.1-alpha.19",
"@backstage/plugin-rollbar-backend": "^0.1.1-alpha.19",
"@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.19",
"@backstage/plugin-sentry-backend": "^0.1.1-alpha.19",
"@backstage/plugin-techdocs-backend": "^0.1.1-alpha.19",
"example-app": "^0.1.1-alpha.20",
"@backstage/backend-common": "^0.1.1-alpha.20",
"@backstage/catalog-model": "^0.1.1-alpha.20",
"@backstage/config": "^0.1.1-alpha.20",
"@backstage/plugin-app-backend": "^0.1.1-alpha.20",
"@backstage/plugin-auth-backend": "^0.1.1-alpha.20",
"@backstage/plugin-catalog-backend": "^0.1.1-alpha.20",
"@backstage/plugin-graphql-backend": "^0.1.1-alpha.20",
"@backstage/plugin-identity-backend": "^0.1.1-alpha.20",
"@backstage/plugin-proxy-backend": "^0.1.1-alpha.20",
"@backstage/plugin-rollbar-backend": "^0.1.1-alpha.20",
"@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.20",
"@backstage/plugin-sentry-backend": "^0.1.1-alpha.20",
"@backstage/plugin-techdocs-backend": "^0.1.1-alpha.20",
"@octokit/rest": "^18.0.0",
"dockerode": "^3.2.0",
"express": "^4.17.1",
@@ -40,7 +42,7 @@
"winston": "^3.2.1"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.19",
"@backstage/cli": "^0.1.1-alpha.20",
"@types/dockerode": "^2.5.32",
"@types/express": "^4.17.6",
"@types/express-serve-static-core": "^4.17.5",
+5 -2
View File
@@ -40,6 +40,7 @@ import sentry from './plugins/sentry';
import proxy from './plugins/proxy';
import techdocs from './plugins/techdocs';
import graphql from './plugins/graphql';
import app from './plugins/app';
import { PluginEnvironment } from './types';
function makeCreateEnv(loadedConfigs: AppConfig[]) {
@@ -71,6 +72,7 @@ async function main() {
const sentryEnv = useHotMemoize(module, () => createEnv('sentry'));
const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs'));
const graphqlEnv = useHotMemoize(module, () => createEnv('graphql'));
const appEnv = useHotMemoize(module, () => createEnv('app'));
const service = createServiceBuilder(module)
.loadConfig(configReader)
@@ -82,8 +84,9 @@ async function main() {
.addRouter('/auth', await auth(authEnv))
.addRouter('/identity', await identity(identityEnv))
.addRouter('/techdocs', await techdocs(techdocsEnv))
.addRouter('/proxy', await proxy(proxyEnv))
.addRouter('/graphql', await graphql(graphqlEnv));
.addRouter('/proxy', await proxy(proxyEnv, '/proxy'))
.addRouter('/graphql', await graphql(graphqlEnv))
.addRouter('', await app(appEnv));
await service.start().catch(err => {
console.log(err);
+25
View File
@@ -0,0 +1,25 @@
/*
* 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 { createRouter } from '@backstage/plugin-app-backend';
import { PluginEnvironment } from '../types';
export default async function createPlugin({ logger }: PluginEnvironment) {
return await createRouter({
logger,
appPackageName: 'example-app',
});
}
+6 -5
View File
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// @ts-ignore
import { createRouter } from '@backstage/plugin-proxy-backend';
import { PluginEnvironment } from '../types';
export default async function createPlugin({
logger,
config,
}: PluginEnvironment) {
return await createRouter({ logger, config });
export default async function createPlugin(
{ logger, config }: PluginEnvironment,
pathPrefix: string,
) {
return await createRouter({ logger, config, pathPrefix });
}
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/catalog-model",
"version": "0.1.1-alpha.19",
"version": "0.1.1-alpha.20",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -20,7 +20,7 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/config": "^0.1.1-alpha.19",
"@backstage/config": "^0.1.1-alpha.20",
"@types/json-schema": "^7.0.5",
"@types/yup": "^0.28.2",
"json-schema": "^0.2.5",
@@ -29,7 +29,7 @@
"yup": "^0.29.1"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.19",
"@backstage/cli": "^0.1.1-alpha.20",
"@types/express": "^4.17.6",
"@types/jest": "^26.0.7",
"@types/lodash": "^4.14.151",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/cli-common",
"description": "Common functionality used by cli, backend, and create-app",
"version": "0.1.1-alpha.19",
"version": "0.1.1-alpha.20",
"private": false,
"main": "src/index.ts",
"types": "src/index.ts",
+3
View File
@@ -28,6 +28,9 @@ module.exports = {
env: {
jest: true,
},
globals: {
__non_webpack_require__: 'readonly',
},
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
+50 -37
View File
@@ -25,43 +25,6 @@ async function getConfig() {
return require(path.resolve('jest.config.ts'));
}
const options = {
rootDir: path.resolve('src'),
coverageDirectory: path.resolve('coverage'),
collectCoverageFrom: ['**/*.{js,jsx,ts,tsx}', '!**/*.d.ts'],
moduleNameMapper: {
'\\.(css|less|scss|sss|styl)$': require.resolve('jest-css-modules'),
},
// We build .esm.js files with plugin:build, so to be able to load these in tests they need to be transformed
// TODO: jest is working on module support, it's possible that we can remove this in the future
transform: {
'\\.esm\\.js$': require.resolve('jest-esm-transformer'),
'\\.(js|jsx|ts|tsx)$': [
require.resolve('ts-jest'),
{ isolatedModules: true },
],
'\\.(bmp|gif|jpg|jpeg|png|frag|xml|svg)$': require.resolve(
'./jestFileTransform.js',
),
},
// A bit more opinionated
testMatch: ['**/?(*.)test.{js,jsx,mjs,ts,tsx}'],
// Default behaviour is to not apply transforms for node_modules, but we still want
// to apply the esm-transformer to .esm.js files, since that's what we use in backstage packages.
// The @kyma-project/asyncapi-react library needs to be transformed.
transformIgnorePatterns: [
'/node_modules/(?!@kyma-project/asyncapi-react/)(?!.*\\.(?:esm\\.js|bmp|gif|jpg|jpeg|png|frag|xml|svg)$)',
],
};
// Use src/setupTests.ts as the default location for configuring test env
if (fs.existsSync('src/setupTests.ts')) {
options.setupFilesAfterEnv = ['<rootDir>/setupTests.ts'];
}
// We read all "jest" config fields in package.json files all the way to the filesystem root.
// All configs are merged together to create the final config, with longer paths taking precedence.
// The merging of the configs is shallow, meaning e.g. all transforms are replaced if new ones are defined.
@@ -92,6 +55,56 @@ async function getConfig() {
currentPath = newPath;
}
// We add an additional Jest config parameter only known by the Backstage CLI
// called `transformModules`. It's a list of modules that we want to apply
// our configured jest transformations for.
// This is useful when packages are published in untranspiled ESM or TS form.
const transformModules = pkgJsonConfigs
.flatMap(conf => {
const modules = conf.transformModules || [];
delete conf.transformModules;
return modules;
})
.map(name => `${name}/`)
.join('|');
const transformModulePattern = transformModules && `(?!${transformModules})`;
const options = {
rootDir: path.resolve('src'),
coverageDirectory: path.resolve('coverage'),
collectCoverageFrom: ['**/*.{js,jsx,ts,tsx}', '!**/*.d.ts'],
moduleNameMapper: {
'\\.(css|less|scss|sss|styl)$': require.resolve('jest-css-modules'),
},
// We build .esm.js files with plugin:build, so to be able to load these in tests they need to be transformed
// TODO: jest is working on module support, it's possible that we can remove this in the future
transform: {
'\\.esm\\.js$': require.resolve('jest-esm-transformer'),
'\\.(js|jsx|ts|tsx)$': [
require.resolve('ts-jest'),
{ isolatedModules: true },
],
'\\.(bmp|gif|jpg|jpeg|png|frag|xml|svg)$': require.resolve(
'./jestFileTransform.js',
),
},
// A bit more opinionated
testMatch: ['**/?(*.)test.{js,jsx,mjs,ts,tsx}'],
// Default behaviour is to not apply transforms for node_modules, but we still want
// to apply the esm-transformer to .esm.js files, since that's what we use in backstage packages.
transformIgnorePatterns: [
`/node_modules/${transformModulePattern}(?:(?!\\.esm).)*\\.(?:js|json)$`,
],
};
// Use src/setupTests.ts as the default location for configuring test env
if (fs.existsSync('src/setupTests.ts')) {
options.setupFilesAfterEnv = ['<rootDir>/setupTests.ts'];
}
return Object.assign(options, ...pkgJsonConfigs);
}
+7 -7
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/cli",
"description": "CLI for developing Backstage plugins and apps",
"version": "0.1.1-alpha.19",
"version": "0.1.1-alpha.20",
"private": false,
"publishConfig": {
"access": "public"
@@ -28,9 +28,9 @@
"backstage-cli": "bin/backstage-cli"
},
"dependencies": {
"@backstage/cli-common": "^0.1.1-alpha.19",
"@backstage/config": "^0.1.1-alpha.19",
"@backstage/config-loader": "^0.1.1-alpha.19",
"@backstage/cli-common": "^0.1.1-alpha.20",
"@backstage/config": "^0.1.1-alpha.20",
"@backstage/config-loader": "^0.1.1-alpha.20",
"@hot-loader/react-dom": "^16.13.0",
"@lerna/package-graph": "^3.18.5",
"@lerna/project": "^3.18.0",
@@ -40,17 +40,17 @@
"@rollup/plugin-yaml": "^2.1.1",
"@spotify/eslint-config": "^7.0.1",
"@sucrase/webpack-loader": "^2.0.0",
"@svgr/plugin-jsx": "4.3.x",
"@svgr/plugin-jsx": "5.4.x",
"@svgr/plugin-svgo": "4.3.x",
"@svgr/rollup": "5.4.x",
"@svgr/webpack": "4.3.x",
"@svgr/webpack": "5.4.x",
"@types/start-server-webpack-plugin": "^2.2.0",
"@types/webpack-env": "^1.15.2",
"@types/webpack-node-externals": "^2.5.0",
"bfj": "^7.0.2",
"chalk": "^4.0.0",
"chokidar": "^3.3.1",
"commander": "^4.1.1",
"commander": "^6.1.0",
"css-loader": "^3.5.3",
"dashify": "^2.0.0",
"diff": "^4.0.2",
+14 -4
View File
@@ -88,15 +88,25 @@ export const makeConfigs = async (
}),
resolve({ mainFields }),
commonjs({
include: ['node_modules/**', '../../node_modules/**'],
exclude: ['**/*.stories.*', '**/*.test.*'],
include: /node_modules/,
exclude: [/\/[^/]+\.(?:stories|test)\.[^/]+$/],
}),
postcss(),
imageFiles({ exclude: '**/*.icon.svg' }),
imageFiles({
exclude: /\.icon\.svg$/,
include: [
/\.css$/,
/\.svg$/,
/\.png$/,
/\.gif$/,
/\.jpg$/,
/\.jpeg$/,
],
}),
json(),
yaml(),
svgr({
include: '**/*.icon.svg',
include: /\.icon\.svg$/,
template: svgrTemplate,
}),
esbuild({
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/config-loader",
"description": "Config loading functionality used by Backstage backend, and CLI",
"version": "0.1.1-alpha.19",
"version": "0.1.1-alpha.20",
"private": false,
"publishConfig": {
"access": "public",
@@ -30,7 +30,7 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/config": "^0.1.1-alpha.19",
"@backstage/config": "^0.1.1-alpha.20",
"fs-extra": "^9.0.0",
"yaml": "^1.9.2",
"yup": "^0.29.1"
+1
View File
@@ -14,5 +14,6 @@
* limitations under the License.
*/
export { readEnvConfig } from './lib';
export { loadConfig } from './loader';
export type { LoadConfigOptions } from './loader';
+21 -21
View File
@@ -14,16 +14,16 @@
* limitations under the License.
*/
import { readEnv } from './env';
import { readEnvConfig } from './env';
describe('readEnv', () => {
describe('readEnvConfig', () => {
it('should return empty config for empty env', () => {
expect(readEnv({})).toEqual([]);
expect(readEnvConfig({})).toEqual([]);
});
it('should return empty config for no matching keys', () => {
expect(
readEnv({
readEnvConfig({
NODE_ENV: 'production',
NOPE_ENV: 'development',
APP_CONFIG: 'foo',
@@ -34,7 +34,7 @@ describe('readEnv', () => {
it('should create config from env', () => {
expect(
readEnv({
readEnvConfig({
NODE_ENV: 'production',
APP_CONFIG_foo: '"bar"',
APP_CONFIG_numbers_a: '1',
@@ -57,22 +57,22 @@ describe('readEnv', () => {
});
it('should accept string values', () => {
expect(readEnv({ APP_CONFIG_foo: '"abc"', APP_CONFIG_bar: 'xyz' })).toEqual(
[
{
data: {
foo: 'abc',
bar: 'xyz',
},
context: 'env',
expect(
readEnvConfig({ APP_CONFIG_foo: '"abc"', APP_CONFIG_bar: 'xyz' }),
).toEqual([
{
data: {
foo: 'abc',
bar: 'xyz',
},
],
);
context: 'env',
},
]);
});
it('should accept complex objects', () => {
expect(
readEnv({
readEnvConfig({
APP_CONFIG_foo: '{ "a": 123, "b": "123", "c": [] }',
APP_CONFIG_bar: '[123, "abc", {}]',
}),
@@ -95,7 +95,7 @@ describe('readEnv', () => {
['APP_CONFIG_fo o'],
['APP_CONFIG_foo_(foo)_foo'],
])('should reject invalid key %p', key => {
expect(() => readEnv({ [key]: '0' })).toThrow(
expect(() => readEnvConfig({ [key]: '0' })).toThrow(
`Invalid env config key '${key.replace('APP_CONFIG_', '')}'`,
);
});
@@ -103,7 +103,7 @@ describe('readEnv', () => {
it.each([['hello'], ['"hello'], ['{'], ['}']])(
'should fallback to string when invalid json value %p',
value => {
expect(readEnv({ APP_CONFIG_foo: value })).toEqual([
expect(readEnvConfig({ APP_CONFIG_foo: value })).toEqual([
{
data: {
foo: value,
@@ -116,7 +116,7 @@ describe('readEnv', () => {
it('should not allow null as a value', () => {
expect(() =>
readEnv({
readEnvConfig({
APP_CONFIG_foo: 'null',
}),
).toThrow(
@@ -126,7 +126,7 @@ describe('readEnv', () => {
it('should not allow duplicate values', () => {
expect(() =>
readEnv({
readEnvConfig({
APP_CONFIG_foo_bar: '1',
APP_CONFIG_foo_bar_baz: '2',
}),
@@ -137,7 +137,7 @@ describe('readEnv', () => {
it('should not allow mixing of objects and other values', () => {
expect(() =>
readEnv({
readEnvConfig({
APP_CONFIG_nested_foo: '1',
APP_CONFIG_nested: '2',
}),
+1 -1
View File
@@ -39,7 +39,7 @@ const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z][a-z0-9]*)*$/i;
*
* APP_CONFIG_app_title='"My Title"'
*/
export function readEnv(env: {
export function readEnvConfig(env: {
[name: string]: string | undefined;
}): AppConfig[] {
let data: JsonObject | undefined = undefined;
+1 -1
View File
@@ -16,5 +16,5 @@
export { resolveStaticConfig } from './resolver';
export { readConfigFile } from './reader';
export { readEnv } from './env';
export { readEnvConfig } from './env';
export { readSecret } from './secrets';
+2 -2
View File
@@ -20,7 +20,7 @@ import { AppConfig, JsonObject } from '@backstage/config';
import {
resolveStaticConfig,
readConfigFile,
readEnv,
readEnvConfig,
readSecret,
} from './lib';
@@ -102,7 +102,7 @@ export async function loadConfig(
);
}
configs.push(...readEnv(process.env));
configs.push(...readEnvConfig(process.env));
return configs;
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/config",
"description": "Config API used by Backstage core, backend, and CLI",
"version": "0.1.1-alpha.19",
"version": "0.1.1-alpha.20",
"private": false,
"publishConfig": {
"access": "public",
+5 -5
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/core-api",
"description": "Internal Core API used by Backstage plugins and apps",
"version": "0.1.1-alpha.19",
"version": "0.1.1-alpha.20",
"private": false,
"publishConfig": {
"access": "public",
@@ -29,8 +29,8 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/config": "^0.1.1-alpha.19",
"@backstage/theme": "^0.1.1-alpha.19",
"@backstage/config": "^0.1.1-alpha.20",
"@backstage/theme": "^0.1.1-alpha.20",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@types/react": "^16.9",
@@ -41,8 +41,8 @@
"zen-observable": "^0.8.15"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.19",
"@backstage/test-utils-core": "^0.1.1-alpha.19",
"@backstage/cli": "^0.1.1-alpha.20",
"@backstage/test-utils-core": "^0.1.1-alpha.20",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
@@ -0,0 +1,47 @@
/*
* 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 '../ApiRef';
/**
* The discovery API is used to provide a mechanism for plugins to
* discover the endpoint to use to talk to their backend counterpart.
*
* The purpose of the discovery API is to allow for many different deployment
* setups and routing methods through a central configuration, instead
* of letting each individual plugin manage that configuration.
*
* Implementations of the discovery API can be a simple as a URL pattern
* using the pluginId, but could also have overrides for individual plugins,
* or query a separate discovery service.
*/
export type DiscoveryApi = {
/**
* Returns the HTTP base backend URL for a given plugin, without a trailing slash.
*
* This method must always be called just before making a request. as opposed to
* fetching the URL when constructing an API client. That is to ensure that more
* flexible routing patterns can be supported.
*
* For example, asking for the URL for `auth` may return something
* like `https://backstage.example.com/api/auth`
*/
getBaseUrl(pluginId: string): Promise<string>;
};
export const discoveryApiRef = createApiRef<DiscoveryApi>({
id: 'core.discovery',
description: 'Provides service discovery of backend plugins',
});
@@ -27,6 +27,7 @@ export * from './AppThemeApi';
export * from './ConfigApi';
export * from './ErrorApi';
export * from './FeatureFlagsApi';
export * from './DiscoveryApi';
export * from './IdentityApi';
export * from './OAuthRequestApi';
export * from './StorageApi';
@@ -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 { UrlPatternDiscovery } from './UrlPatternDiscovery';
describe('UrlPatternDiscovery', () => {
it('should not require interpolation', async () => {
const discoveryApi = UrlPatternDiscovery.compile('http://example.com');
await expect(discoveryApi.getBaseUrl('my-plugin')).resolves.toBe(
'http://example.com',
);
});
it('should use a plain pattern', async () => {
const discoveryApi = UrlPatternDiscovery.compile(
'http://localhost:7000/{{ pluginId }}',
);
await expect(discoveryApi.getBaseUrl('my-plugin')).resolves.toBe(
'http://localhost:7000/my-plugin',
);
});
it('should allow for multiple interpolation points', async () => {
const discoveryApi = UrlPatternDiscovery.compile(
'https://{{pluginId }}.example.com/api/{{ pluginId}}',
);
await expect(discoveryApi.getBaseUrl('my-plugin')).resolves.toBe(
'https://my-plugin.example.com/api/my-plugin',
);
});
it('should validate that the pattern is a valid URL', () => {
expect(() => {
UrlPatternDiscovery.compile('example.com');
}).toThrow('Invalid discovery URL pattern, Invalid URL: example.com');
expect(() => {
UrlPatternDiscovery.compile('http://');
}).toThrow('Invalid discovery URL pattern, Invalid URL: http://');
expect(() => {
UrlPatternDiscovery.compile('abc123');
}).toThrow('Invalid discovery URL pattern, Invalid URL: abc123');
expect(() => {
UrlPatternDiscovery.compile('http://example.com:{{pluginId}}');
}).toThrow(
'Invalid discovery URL pattern, Invalid URL: http://example.com:pluginId',
);
expect(() => {
UrlPatternDiscovery.compile('/{{pluginId}}');
}).toThrow('Invalid discovery URL pattern, Invalid URL: /pluginId');
expect(() => {
UrlPatternDiscovery.compile('http://localhost/{{pluginId}}?forbidden');
}).toThrow('Invalid discovery URL pattern, URL must not have a query');
expect(() => {
UrlPatternDiscovery.compile('http://localhost/{{pluginId}}#forbidden');
}).toThrow('Invalid discovery URL pattern, URL must not have a hash');
expect(() => {
UrlPatternDiscovery.compile('http://localhost/{{pluginId}}/');
}).toThrow('Invalid discovery URL pattern, URL must not end with a slash');
expect(() => {
UrlPatternDiscovery.compile('http://localhost/');
}).toThrow('Invalid discovery URL pattern, URL must not end with a slash');
});
});
@@ -0,0 +1,58 @@
/*
* 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 '../../definitions/DiscoveryApi';
/**
* UrlPatternDiscovery is a lightweight DiscoveryApi implementation.
* It uses a single template string to construct URLs for each plugin.
*/
export class UrlPatternDiscovery implements DiscoveryApi {
/**
* Creates a new UrlPatternDiscovery given a template. The the only
* interpolation done for the template is to replace instances of `{{pluginId}}`
* with the ID of the plugin being requested.
*
* Example pattern: `http://localhost:7000/api/{{ pluginId }}`
*/
static compile(pattern: string): UrlPatternDiscovery {
const parts = pattern.split(/\{\{\s*pluginId\s*\}\}/);
try {
const urlStr = parts.join('pluginId');
const url = new URL(urlStr);
if (url.hash) {
throw new Error('URL must not have a hash');
}
if (url.search) {
throw new Error('URL must not have a query');
}
if (urlStr.endsWith('/')) {
throw new Error('URL must not end with a slash');
}
} catch (error) {
throw new Error(`Invalid discovery URL pattern, ${error.message}`);
}
return new UrlPatternDiscovery(parts);
}
private constructor(private readonly parts: string[]) {}
async getBaseUrl(pluginId: string): Promise<string> {
return this.parts.join(pluginId);
}
}
@@ -0,0 +1,21 @@
/*
* 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.
*/
// This folder contains implementations for all core APIs.
//
// Plugins should rely on these APIs for functionality as much as possible.
export { UrlPatternDiscovery } from './UrlPatternDiscovery';
@@ -27,16 +27,17 @@ import {
AuthRequestOptions,
BackstageIdentity,
} from '../../../definitions/auth';
import { OAuthRequestApi, AuthProvider } from '../../../definitions';
import {
OAuthRequestApi,
AuthProvider,
DiscoveryApi,
} from '../../../definitions';
import { SessionManager } from '../../../../lib/AuthSessionManager/types';
import { RefreshingAuthSessionManager } from '../../../../lib/AuthSessionManager';
import { Observable } from '../../../../types';
type CreateOptions = {
// TODO(Following the words of Rugvip): These two should be grabbed from global config when available, they're not unique to Auth0Auth
backendUrl: string;
basePath: string;
discoveryApi: DiscoveryApi;
oauthRequestApi: OAuthRequestApi;
environment?: string;
@@ -67,15 +68,13 @@ class Auth0Auth
BackstageIdentityApi,
SessionStateApi {
static create({
backendUrl,
basePath,
discoveryApi,
environment = 'development',
provider = DEFAULT_PROVIDER,
oauthRequestApi,
}: CreateOptions) {
const connector = new DefaultAuthConnector({
backendUrl,
basePath,
discoveryApi,
environment,
provider,
oauthRequestApi: oauthRequestApi,
@@ -25,7 +25,11 @@ import {
BackstageIdentity,
AuthRequestOptions,
} from '../../../definitions/auth';
import { OAuthRequestApi, AuthProvider } from '../../../definitions';
import {
OAuthRequestApi,
AuthProvider,
DiscoveryApi,
} from '../../../definitions';
import { SessionManager } from '../../../../lib/AuthSessionManager/types';
import {
AuthSessionStore,
@@ -34,10 +38,7 @@ import {
import { Observable } from '../../../../types';
type CreateOptions = {
// TODO(Rugvip): These two should be grabbed from global config when available, they're not unique to GithubAuth
backendUrl: string;
basePath: string;
discoveryApi: DiscoveryApi;
oauthRequestApi: OAuthRequestApi;
environment?: string;
@@ -62,15 +63,13 @@ const DEFAULT_PROVIDER = {
class GithubAuth implements OAuthApi, SessionStateApi {
static create({
backendUrl,
basePath,
discoveryApi,
environment = 'development',
provider = DEFAULT_PROVIDER,
oauthRequestApi,
}: CreateOptions) {
const connector = new DefaultAuthConnector({
backendUrl,
basePath,
discoveryApi,
environment,
provider,
oauthRequestApi: oauthRequestApi,
@@ -25,15 +25,17 @@ import {
BackstageIdentity,
AuthRequestOptions,
} from '../../../definitions/auth';
import { OAuthRequestApi, AuthProvider } from '../../../definitions';
import {
OAuthRequestApi,
AuthProvider,
DiscoveryApi,
} from '../../../definitions';
import { SessionManager } from '../../../../lib/AuthSessionManager/types';
import { StaticAuthSessionManager } from '../../../../lib/AuthSessionManager';
import { Observable } from '../../../../types';
type CreateOptions = {
backendUrl: string;
basePath: string;
discoveryApi: DiscoveryApi;
oauthRequestApi: OAuthRequestApi;
environment?: string;
@@ -58,15 +60,13 @@ const DEFAULT_PROVIDER = {
class GitlabAuth implements OAuthApi, SessionStateApi {
static create({
backendUrl,
basePath,
discoveryApi,
environment = 'development',
provider = DEFAULT_PROVIDER,
oauthRequestApi,
}: CreateOptions) {
const connector = new DefaultAuthConnector({
backendUrl,
basePath,
discoveryApi,
environment,
provider,
oauthRequestApi,

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