This commit is contained in:
Tim Hansen
2020-09-09 08:51:55 -06:00
519 changed files with 17637 additions and 8099 deletions
+1
View File
@@ -1,5 +1,6 @@
**/node_modules/**
**/dist/**
**/dist-types/**
**/storybook-static/**
**/coverage/**
**/build/**
+26
View File
@@ -0,0 +1,26 @@
---
name: 'RFC'
about: 'Request For Comments (RFC) from the community'
labels: rfc
title: '[RFC] <name>'
---
**Status:** Open for comments
<!--- Open for comments |Closed for comments (RFC no longer maintained) --->
## Need
<!--- Why are we proposing this change? Why is this the problem were trying to address and what benefits/impact do we expect to get from this --->
## Proposal
<!--- The proposed approach. Describe the proposal in as much detail as needed for reviewers to give concrete feedback. Take special care in this section to describe any implications on data privacy or security. --->
## Alternatives
<!--- What alternatives to the proposed solution were considered? What criteria/data was used to discard these --->
## Risks
<!--- What other things happening could conflict or compete (for example for resources) with the proposal? What risk are there and how do we plan to handle them --->
+2
View File
@@ -4,7 +4,9 @@
That makes it easier to understand the change so we can :shipit: faster. -->
#### :heavy_check_mark: Checklist
<!--- Put an `x` in all the boxes that apply: -->
- [ ] All tests are passing `yarn test`
- [ ] Screenshots attached (for UI changes)
- [ ] Relevant documentation updated
+10
View File
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: npm
directory: '/'
schedule:
interval: daily
time: '04:00'
open-pull-requests-limit: 10
labels:
- dependencies
+17 -1
View File
@@ -69,11 +69,14 @@ jobs:
- name: verify doc links
run: node docs/verify-links.js
- name: prettier
run: yarn prettier:check
- name: lint
run: yarn lerna -- run lint --since origin/master
- name: type checking and declarations
run: yarn tsc --incremental false
run: yarn tsc:full
- name: build changed packages
if: ${{ steps.yarn-lock.outcome == 'success' }}
@@ -96,3 +99,16 @@ jobs:
- name: verify plugin template
run: yarn lerna -- run diff -- --check
- name: ensure clean working directory
run: |
if files=$(git ls-files --exclude-standard --others --modified) && [[ -z "$files" ]]; then
exit 0
else
echo ""
echo "Working directory has been modified:"
echo ""
git status --short
echo ""
exit 1
fi
+1 -1
View File
@@ -49,6 +49,6 @@ jobs:
- run: yarn tsc
- name: yarn build
run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli --ignore backstage-microsite
run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli
- name: run E2E test
run: yarn workspace e2e-test start
+1 -1
View File
@@ -64,7 +64,7 @@ jobs:
- run: yarn tsc
- name: yarn build
run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli --ignore backstage-microsite
run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli
- name: run E2E test
run: |
sudo sysctl fs.inotify.max_user_watches=524288
+18 -3
View File
@@ -39,7 +39,22 @@ jobs:
run: yarn install --frozen-lockfile
# End of yarn setup
- name: lint
run: yarn lerna -- run lint
# Tests are broken on Windows, disabled for now
# - name: test
# run: yarn lerna -- run test
- name: type checking and declarations
run: yarn tsc:full
- name: verify type dependencies
run: yarn lint:type-deps
- name: test
run: yarn lerna -- run test
- name: Discord notification
if: ${{ failure() }}
uses: Ilshidur/action-discord@0.2.0
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
with:
args: 'Windows master build failed https://github.com/{{GITHUB_REPOSITORY}}/actions/runs/{{GITHUB_RUN_ID}}'
+4 -4
View File
@@ -51,7 +51,7 @@ jobs:
run: yarn lerna -- run lint
- name: type checking and declarations
run: yarn tsc --incremental false
run: yarn tsc:full
- name: build
run: yarn build
@@ -71,9 +71,9 @@ jobs:
# Tags the commit with the version in the core package if the tag doesn't exist
- uses: Klemensas/action-autotag@1.2.3
with:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
package_root: "packages/core"
tag_prefix: "v"
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
package_root: 'packages/core'
tag_prefix: 'v'
- name: Discord notification
if: ${{ failure() }}
+6 -22
View File
@@ -22,33 +22,17 @@ jobs:
steps:
- uses: actions/checkout@v2
# Beginning of yarn setup, keep in sync between all workflows, see ci.yml
- name: use node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
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') }}
restore-keys: |
${{ runner.os }}-yarn-
# Skip caching of microsite dependencies, it keeps the global cache size
# smaller, which make Windows builds a lot faster for the rest of the project.
- name: yarn install
run: yarn install --frozen-lockfile
# End of yarn setup
working-directory: microsite
- name: build microsite
run: yarn workspace backstage-microsite build
run: yarn build
working-directory: microsite
@@ -26,36 +26,24 @@ jobs:
steps:
- uses: actions/checkout@v2
# Beginning of yarn setup, keep in sync between all workflows, see ci.yml
- name: use node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
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') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: yarn install
# We avoid caching in this workflow, as we're running an install of both the top-level
# dependencies and the microsite. We leave it to the main master workflow to produce the
# cache, as that results in a smaller bundle.
- name: top-level yarn install
run: yarn install --frozen-lockfile
# End of yarn setup
- name: microsite yarn install
run: yarn install --frozen-lockfile
working-directory: microsite
- name: build microsite
run: yarn workspace backstage-microsite build
run: yarn build
working-directory: microsite
- name: build storybook
run: yarn workspace storybook build-storybook
+1 -3
View File
@@ -89,9 +89,7 @@ typings/
# Nuxt.js build / generate output
.nuxt
dist
# Microsite build output
microsite/build
dist-types
# Gatsby files
.cache/
+8
View File
@@ -0,0 +1,8 @@
.yarn
dist
microsite/build
coverage
*.hbs
templates
plugins/scaffolder-backend/sample-templates
.vscode
+1
View File
@@ -12,3 +12,4 @@
| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. |
| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D |
| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling |
| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling |
+14
View File
@@ -8,6 +8,20 @@ If you encounter issues while upgrading to a newer version, don't hesitate to re
> Collect changes for the next release below
- Material-UI: Bumped to 4.11.0, which is the version that create-app will
resolve to, because we wanted to get the renaming of ExpansionPanel to
Accordion into place. This gets rid of a lot of console deprecation warnings
in newly scaffolded apps.
- The backend plugin
[service builder](https://github.com/spotify/backstage/blob/master/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts)
no longer adds `express.json()` automatically to all routes. While convenient
in a lot of cases, it also led to problems where for example the proxy
middleware could hang because the body had already been altered and could not
be streamed. Also, plugins that rather wanted to handle e.g. form encoded data
still had to cater to that manually. We therefore decided to let plugins add
`express.json()` themselves if they happen to deal with JSON data.
## v0.1.1-alpha.20
- Includes https://github.com/spotify/backstage/pull/2097 to resolve issues with create-plugin command.
+15 -16
View File
@@ -4,12 +4,12 @@ This code of conduct outlines our expectations for participants within the **Spo
Our open source community strives to:
* **Be friendly and patient.**
* **Be welcoming**: We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability.
* **Be considerate**: Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary language.
* **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. Its important to remember that a community where people feel uncomfortable or threatened is not a productive one.
* **Be careful in the words that we choose**: we are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren't acceptable.
* **Try to understand why we disagree**: Disagreements, both social and technical, happen all the time. It is important that we resolve disagreements and differing views constructively. Remember that were different. The strength of our community comes from its diversity, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesnt mean that theyre wrong. Dont forget that it is human to err and blaming each other doesnt get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes.
- **Be friendly and patient.**
- **Be welcoming**: We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability.
- **Be considerate**: Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary language.
- **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. Its important to remember that a community where people feel uncomfortable or threatened is not a productive one.
- **Be careful in the words that we choose**: we are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren't acceptable.
- **Try to understand why we disagree**: Disagreements, both social and technical, happen all the time. It is important that we resolve disagreements and differing views constructively. Remember that were different. The strength of our community comes from its diversity, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesnt mean that theyre wrong. Dont forget that it is human to err and blaming each other doesnt get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes.
## Definitions
@@ -18,7 +18,7 @@ Harassment includes, but is not limited to:
- Offensive comments related to gender, gender identity and expression, sexual orientation, disability, mental illness, neuro(a)typicality, physical appearance, body size, race, age, regional discrimination, political or religious affiliation
- Unwelcome comments regarding a persons lifestyle choices and practices, including those related to food, health, parenting, drugs, and employment
- Deliberate misgendering. This includes deadnaming or persistently using a pronoun that does not correctly reflect a person's gender identity. You must address people by the name they give you when not addressing them by their username or handle
- Physical contact and simulated physical contact (eg, textual descriptions like “*hug*” or “*backrub*”) without consent or after a request to stop
- Physical contact and simulated physical contact (eg, textual descriptions like “_hug_” or “_backrub_”) without consent or after a request to stop
- Threats of violence, both physical and psychological
- Incitement of violence towards any individual, including encouraging a person to commit suicide or to engage in self-harm
- Deliberate intimidation
@@ -39,7 +39,6 @@ Our open source community prioritizes marginalized peoples safety over privil
- Communicating in a tone you dont find congenial
- Criticizing racist, sexist, cissexist, or otherwise oppressive behavior or assumptions
### Diversity Statement
We encourage everyone to participate and are committed to building a community for all. Although we will fail at times, we seek to treat everyone both as fairly and equally as possible. Whenever a participant has made a mistake, we expect them to take responsibility for it. If someone has been harmed or offended, it is our responsibility to listen carefully and respectfully, and do our best to right the wrong.
@@ -53,18 +52,18 @@ If you experience or witness unacceptable behavior—or have any other concerns
- Your contact information.
- Names (real, nicknames, or pseudonyms) of any individuals involved. If there are additional witnesses, please
include them as well. Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public IRC logger), please include a link.
include them as well. Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public IRC logger), please include a link.
- Any additional information that may be helpful.
After filing a report, a representative will contact you personally, review the incident, follow up with any additional questions, and make a decision as to how to respond. If the person who is harassing you is part of the response team, they will recuse themselves from handling your incident. If the complaint originates from a member of the response team, it will be handled by a different member of the response team. We will respect confidentiality requests for the purpose of protecting victims of abuse.
### Attribution & Acknowledgements
We all stand on the shoulders of giants across many open source communities. We'd like to thank the communities and projects that established code of conducts and diversity statements as our inspiration:
We all stand on the shoulders of giants across many open source communities. We'd like to thank the communities and projects that established code of conducts and diversity statements as our inspiration:
* [Django](https://www.djangoproject.com/conduct/reporting/)
* [Python](https://www.python.org/community/diversity/)
* [Ubuntu](http://www.ubuntu.com/about/about-ubuntu/conduct)
* [Contributor Covenant](http://contributor-covenant.org/)
* [Geek Feminism](http://geekfeminism.org/about/code-of-conduct/)
* [Citizen Code of Conduct](http://citizencodeofconduct.org/)
- [Django](https://www.djangoproject.com/conduct/reporting/)
- [Python](https://www.python.org/community/diversity/)
- [Ubuntu](http://www.ubuntu.com/about/about-ubuntu/conduct)
- [Contributor Covenant](http://contributor-covenant.org/)
- [Geek Feminism](http://geekfeminism.org/about/code-of-conduct/)
- [Citizen Code of Conduct](http://citizencodeofconduct.org/)
+2 -5
View File
@@ -1,7 +1,4 @@
---
id: CONTRIBUTING
title: Contributing
---
# Contributing to Backstage
Our vision for Backstage is for it to become the trusted standard toolbox (read: UX layer) for the open source infrastructure landscape. Think of it like Kubernetes for developer experience. We realize this is an ambitious goal. We cant do it alone.
@@ -31,7 +28,7 @@ What kind of plugins should/could be created? Some inspiration from the 120+ plu
## Suggesting a plugin
If you start developing a plugin that you aim to release as open source, we suggest that you create a new [new Issue](https://github.com/spotify/backstage/issues/new?template=plugin_template.md). This helps the community know what plugins are in development.
If you start developing a plugin that you aim to release as open source, we suggest that you create a new [new Issue](https://github.com/spotify/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME). This helps the community know what plugins are in development.
You can also use this process if you have an idea for a good plugin but you hope that someone else will pick up the work.
+6 -1
View File
@@ -1,9 +1,14 @@
FROM nginx:mainline
# The purpose of this image is to serve the frontend app content separately.
# By default the Backstage backend uses the app-backend plugin to serve the
# app from the backend itself, but it may be desirable to move the frontend
# content serving to a separate deployment, in which case this image can be used.
# This dockerfile requires the app to be built on the host first, as it
# simply copies in the build output into the image.
# The safest way to build this image is to use `yarn docker-build`
# The safest way to build this image is to use `yarn docker-build:app`
RUN apt-get update && apt-get -y install jq && rm -rf /var/lib/apt/lists/*
+1 -1
View File
@@ -61,7 +61,7 @@ Take a look at the [Getting Started](https://backstage.io/docs/getting-started/i
## Documentation
- [Main documentation](https://backstage.io/docs/overview/what-is-backstage)
- [Main documentation](https://backstage.io/docs)
- [Service Catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview)
- [Architecture](https://backstage.io/docs/overview/architecture-terminology) ([Decisions](https://backstage.io/docs/architecture-decisions/adrs-overview))
- [Designing for Backstage](https://backstage.io/docs/dls/design)
+11
View File
@@ -0,0 +1,11 @@
app:
baseUrl: http://localhost:3000
backend:
baseUrl: http://localhost:7000
listen:
port: 7000
cors:
origin: http://localhost:3000
methods: [GET, POST, PUT, DELETE]
credentials: true
+19 -10
View File
@@ -1,22 +1,27 @@
app:
title: Backstage Example App
baseUrl: http://localhost:3000
baseUrl: http://localhost:7000
backend:
baseUrl: http://localhost:7000
listen:
port: 7000
cors:
origin: http://localhost:3000
methods: [GET, POST, PUT, DELETE]
credentials: true
database:
client: sqlite3
connection: ':memory:'
# See README.md in the proxy-backend plugin for information on the configuration format
proxy:
'/circleci/api': https://circleci.com/api/v1.1
'/circleci/api':
target: https://circleci.com/api/v1.1
changeOrigin: true
pathRewrite:
'^/proxy/circleci/api/': '/'
headers:
Circle-Token:
$secret:
env: CIRCLECI_AUTH_TOKEN
'/jenkins/api':
target: http://localhost:8080
headers:
@@ -29,6 +34,7 @@ organization:
techdocs:
storageUrl: http://localhost:7000/techdocs/static/docs
requestUrl: http://localhost:7000/techdocs/docs
sentry:
organization: spotify
@@ -48,6 +54,8 @@ lighthouse:
baseUrl: http://localhost:3003
catalog:
rules:
- allow: [Component, API, Group, Template, Location]
processors:
githubApi:
privateToken:
@@ -83,6 +91,8 @@ catalog:
- 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
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/hello-world-api.yaml
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/streetlights-api.yaml
auth:
providers:
@@ -116,10 +126,9 @@ auth:
audience:
$secret:
env: GITLAB_BASE_URL
# saml:
# development:
# entryPoint: "http://localhost:7001/"
# issuer: "passport-saml"
saml:
entryPoint: 'http://localhost:7001/'
issuer: 'passport-saml'
okta:
development:
clientId:
+1 -2
View File
@@ -3,10 +3,9 @@ kind: Component
metadata:
name: backstage
description: |
Backstage is an open-source developer portal that puts the developer experience first.
Backstage is an open-source developer portal that puts the developer experience first.
annotations:
github.com/project-slug: spotify/backstage
backstage.io/github-actions-id: spotify/backstage
backstage.io/techdocs-ref: github:https://github.com/spotify/backstage.git
spec:
type: library
+2 -6
View File
@@ -1,14 +1,10 @@
# Make sure that before you
# run the docker-compose that you have run
# $ yarn docker-build:all
# $ yarn docker-build
version: '3'
services:
frontend:
image: 'spotify/backstage:latest'
ports:
- '3000:80'
backend:
backstage:
image: 'example-backend:latest'
ports:
- '7000:7000'
+126 -52
View File
@@ -17,7 +17,7 @@ Utility APIs. While the `createPlugin` API is focused on the initialization
plugins and the app, the Utility APIs provide ways for plugins to communicate
during their entire life cycle.
## Usage
## Consuming APIs
Each Utility API is tied to an `ApiRef` instance, which is a global singleton
object without any additional state or functionality, its only purpose is to
@@ -55,52 +55,112 @@ from any component inside Backstage, including the ones in `@backstage/core`.
The only requirement is that they are beneath the `AppProvider` in the react
tree.
## Registering Utility API Implementations
## Supplying APIs
The Backstage App is responsible for providing implementations for all Utility
APIs required by plugins. The example app in this repo registers its APIs inside
[src/apis.ts](/packages/app/src/apis.ts). Here's an example of how to wire up
the `ErrorApi` inside an app:
### API Factories
APIs are registered in the form of `ApiFactories`, which encapsulate the process
of instantiating an API. It is a collection of three things: the `ApiRef` of the
API to instantiate, a list of all required dependencies, and a factory function
that returns a new API instance.
For example, this is the default `ApiFactory` for the `ErrorApi`:
```ts
import {
ApiRegistry,
createApp,
alertApiRef,
errorApiRef,
AlertApiForwarder,
ErrorApiForwarder,
ErrorAlerter,
ConfigApi
} from '@backstage/core';
const apis = (config: ConfigApi) => {
const builder = ApiRegistry.builder();
// The alert API is a self-contained implementation that shows alerts to the user.
const alertApi = builder.add(alertApiRef, new AlertApiForwarder());
// The error API uses the alert API to send error notifications to the user.
builder.add(errorApiRef, new ErrorAlerter(alertApi, new ErrorApiForwarder()));
return builder.build();
}
const app = createApp({
apis,
// ... other config
createApiFactory({
api: errorApiRef,
deps: { alertApi: alertApiRef },
factory: ({ alertApi }) =>
new ErrorAlerter(alertApi, new ErrorApiForwarder()),
});
```
The `ApiRegistry` is used to register all Utility APIs in the app and associate
them with `ApiRef`s. It implements the `ApiHolder` interface, which enables it
to provide an API implementation given an `ApiRef`.
In this example the `errorApiRef` is our API, which encapsulates the `ErrorApi`
type. The `alertApiRef` is our single dependency, which we give the name
`alertApi`, and is then passed on to the factory function, which returns an
implementation of the `ErrorApi`.
Note that our `ErrorApi` implementation depends on another Utility API, the
`AlertApi`. This is the method with which APIs can depend on other APIs, using
manual dependency injection at the initialization of the app. In general, if you
want to depend on another Utility API in an implementation of an API, you import
the type for that API and make it a constructor parameter.
The `createApiFactory` function is a thin wrapper that enables TypeScript type
inference. You may notice that there are no type annotations in the above
example, and that is because we're able to infer all types from the `ApiRef`s.
TypeScript will make sure that the return value of the `factory` function
matches the type embedded in `api`'s `ApiRef`, in this case the `ErrorApi`. It
will also match the types between the `deps` and the parameters of the `factory`
function, again using the type embedded within the `ApiRef`s.
## Registering API Factories
The responsibility for adding Utility APIs to a Backstage app lies in three
different locations: the Backstage core library, each plugin included in the
app, and the app itself.
### Core APIs
Starting with the Backstage core library, it provides implementation for all of
the core APIs. The core APIs are the ones exported by `@backstage/core`, such as
the `errorApiRef` and `configApiRef`. You can find a full list of them
[here](../reference/utility-apis/README.md).
The core APIs are loaded for any app created with `createApp` from
`@backstage/core`, which means that there is no step that needs to be taken to
include these APIs in an app.
### Plugin APIs
In addition to the core APIs, plugins can define and export their own APIs.
While doing so they should usually also provide default implementations of their
own APIs, for example, the `catalog` plugin exports `catalogApiRef`, and also
supplies a default `ApiFactory` of that API using the `CatalogClient`. There is
one restriction to plugin-provided API Factories: plugins may not supply
factories for core APIs, trying to do so will cause the app to crash.
Plugins supply their APIs through the `apis` option of `createPlugin`, for
example:
```ts
export const plugin = createPlugin({
id: 'techdocs',
apis: [
createApiFactory({
api: techdocsStorageApiRef,
deps: { configApi: configApiRef },
factory({ configApi }) {
return new TechDocsStorageApi({
apiOrigin: configApi.getString('techdocs.storageUrl'),
});
},
}),
],
});
```
### App APIs
Lastly, the app itself is the final point where APIs can be added, and what has
the final say in what APIs will be loaded at runtime. The app may override the
factories for any of the core or plugin APIs, with the exception of the config,
app theme, and identity APIs. These are static APIs that are tied into the
`createApp` implementation, and therefore not possible to override.
Overriding APIs is useful for apps that want to switch out behavior to tailor it
to their environment. In some cases plugins may also export multiple
implementations of the same API, where they each have their own different
requirements on for example backend storage and surrounding environment.
Supplying APIs to the app works just like for plugins:
```ts
const app = createApp({
apis: [
/* ApiFactories */
],
// ... other options
});
```
A common pattern is to export a list of all APIs from `apis.ts`, next to
`App.tsx`. See the [example app in this repo](../../packages/app/src/apis.ts)
for an example.
## Custom implementations of Utility APIs
@@ -128,19 +188,33 @@ implement the `ErrorApi`, as it is checked by the type embedded in the
## Defining custom Utility APIs
The pattern for plugins defining their own Utility APIs is not fully established
yet. The current way is for the plugin to export its own `ApiRef` and type for
the API, along with one or more implementations. It is then up to the app to
import, and register those APIs. See for example the
[lighthouse](/plugins/lighthouse/src/api.ts) or
[graphiql](/plugins/graphiql/src/lib/api/types.ts) plugins for examples of this.
Plugins are free to define their own Utility APIs. Simply define the TypeScript
interface for the API, and create an `ApiRef` using `createApiRef` exported from
`@backstage/core`. Also be sure to provide at least one implementation of the
API, and to declare a default factory for the API in `createPlugin`.
The goal is to make this process a bit smoother, but that requires work in other
parts of Backstage, like configuration management. So it remains as a TODO. If
you have more questions regarding this, or have an idea for an API that you want
to share outside your plugin, hit us up in
[GitHub issues](https://github.com/spotify/backstage/issues/new/choose) or the
[Backstage Discord server](https://discord.gg/EBHEGzX).
Custom Utility APIs can be either public or private, which it is up to the
plugin to choose. Private APIs do not expose an external API surface, and it's
therefore possible to make breaking changes to the API without affecting other
users of the plugin. If an API is made public however, it opens up for other
plugins to make use of the API, and it also makes it possible for users for your
plugin to override the API in the app. It is however important to maintain
backwards compatibility of public APIs, as you may otherwise break apps that are
using your plugin.
To make an API public, simply export the `ApiRef` of the API, and any associated
types. To make an API private, just avoid exporting the `ApiRef`, but still be
sure to supply a default factory to `createPlugin`.
Private APIs are useful for plugins that want to depend on other APIs outside of
React components, but not have to expose an entire API surface to maintain. When
using private APIs, it is fine to use the `typeof` of an implementing class as
the type parameter passed to `createApiRef`, while public APIs should always
define a separate TypeScript interface type.
Plugins may depend on APIs from other plugins, both in React components and as
dependencies to API factories. Do however be sure to not cause circular
dependencies between plugins.
## Architecture
@@ -68,7 +68,7 @@ metadata:
lifecycle: production
example.com/service-discovery-name: frobsawesome
annotations:
circleci.com/project-slug: gh/example-org/frobs-awesome
circleci.com/project-slug: github/example-org/frobs-awesome
spec:
type: service
```
+1 -1
View File
@@ -4,7 +4,7 @@ title: Architecture Decision Records (ADR)
sidebar_label: Overview
---
The substantial architecture decisions made in the Backstage project lives here.
The substantial architecture decisions made in the Backstage project live 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/).
Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 KiB

After

Width:  |  Height:  |  Size: 102 KiB

+104 -53
View File
@@ -48,22 +48,35 @@ provider class which implements a handler for the chosen framework.
#### Adding an OAuth based provider
If we're adding an `OAuth` based provider we would implement the
[OAuthProviderHandlers](#OAuthProviderHandlers) interface.
[OAuthProviderHandlers](#OAuthProviderHandlers) interface. By implementing this
interface we can use the `OAuthProvider` class provided by `lib/oauth`, meaning
we don't need to implement the full
[AuthProviderRouteHandlers](#AuthProviderRouteHandlers) interface that providers
otherwise need to implement.
The provider class takes the provider's configuration as a class parameter. It
also imports the `Strategy` from the passport package.
The provider class takes the provider's options as a class parameter. It also
imports the `Strategy` from the passport package.
```ts
import { Strategy as ProviderAStrategy } from 'passport-provider-a';
export type ProviderAProviderOptions = OAuthProviderOptions & {
// extra options here
}
export class ProviderAAuthProvider implements OAuthProviderHandlers {
private readonly providerConfig: AuthProviderConfig;
private readonly _strategy: ProviderAStrategy;
constructor(providerConfig: AuthProviderConfig) {
this.providerConfig = providerConfig;
constructor(options: ProviderAProviderOptions) {
this._strategy = new ProviderAStrategy(
{ ...providerConfig.options },
{
clientID: options.clientId,
clientSecret: options.clientSecret,
callbackURL: options.callbackUrl,
passReqToCallback: false as true,
response_type: 'code',
/// ... etc
}
verifyFunction, // See the "Verify Callback" section
);
}
@@ -82,14 +95,18 @@ An non-`OAuth` based provider could implement
[AuthProviderRouteHandlers](#AuthProviderRouteHandlers) instead.
```ts
type ProviderAOptions = {
// ...
};
export class ProviderAAuthProvider implements AuthProviderRouteHandlers {
private readonly providerConfig: AuthProviderConfig;
private readonly _strategy: ProviderAStrategy;
constructor(providerConfig: AuthProviderConfig) {
this.providerConfig = providerConfig;
constructor(options: ProviderAOptions) {
this._strategy = new ProviderAStrategy(
{ ...providerConfig.options },
{
// ...
},
verifyFunction, // See the "Verify Callback" section
);
}
@@ -101,31 +118,61 @@ export class ProviderAAuthProvider implements AuthProviderRouteHandlers {
}
```
#### Create method
#### Factory function
Each provider exports a create method that creates the provider instance,
optionally extending a supported authorization framework. This method exists to
allow for flexibility if additional frameworks are supported in the future.
Each provider exports a factory function that instantiates the provider. The
factory should implement [AuthProviderFactory](#AuthProviderFactory), which
passes in a object with utilities for configuration, logging, token issuing,
etc. The factory should return an implementation of
[AuthProviderRouteHandlers](#AuthProviderRouteHandlers).
Implementing OAuth by returning an instance of `OAuthProvider` based of the
provider's class:
The factory is what decides the mapping from
[static configuration](../conf/index.md) to the creation of auth providers. For
example, OAuth providers use `OAuthEnvironmentHandler` to allow for multiple
different configurations, one for each environment, which looks like this;
```ts
export function createProviderAProvider(config: AuthProviderConfig) {
const provider = new ProviderAAuthProvider(config);
const oauthProvider = new OAuthProvider(provider, config.provider, true);
return oauthProvider;
}
export const createOktaProvider: AuthProviderFactory = ({
globalConfig,
config,
tokenIssuer,
}) =>
OAuthEnvironmentHandler.mapConfig(config, envConfig => {
// read options from config
const clientId = envConfig.getString('clientId');
const clientSecret = envConfig.getString('clientSecret');
// instantiate our OAuthProviderHandlers implementation
const provider = new OktaAuthProvider({
audience,
clientId,
clientSecret,
callbackUrl,
});
// Wrap the OAuthProviderHandlers with OAuthProvider, which implements AuthProviderRouteHandlers
return OAuthProvider.fromConfig(globalConfig, provider, {
disableRefresh: false,
providerId,
tokenIssuer,
});
});
```
Not extending with OAuth, the main difference here is that the create method is
returning a instance of the class without adding the OAuth authorization
framework to it.
The purpose of the different environments is to allow for a single auth-backend
to serve as the authentication service for multiple different frontend
environments, such as local development, staging, and production.
The factory function for other providers can be a lot simpler, as they might not
have configuration for each environment. Looking something like this:
```ts
export function createProviderAProvider(config: AuthProviderConfig) {
return new ProviderAAuthProvider(config);
}
export const createProviderAProvider: AuthProviderFactory = ({ config }) => {
const a = config.getString('a');
const b = config.getString('b');
return new ProviderAAuthProvider({ a, b });
};
```
#### Verify Callback
@@ -144,7 +191,7 @@ export function createProviderAProvider(config: AuthProviderConfig) {
> http://www.passportjs.org/docs/configure/
**`plugins/auth-backend/src/providers/providerA/index.ts`** is simply
re-exporting the create method to be used for hooking the provider up to the
re-exporting the factory function to be used for hooking the provider up to the
backend.
```ts
@@ -153,26 +200,14 @@ export { createProviderAProvider } from './provider';
### Hook it up to the backend
**`plugins/auth-backend/src/providers/config.ts`** The provider needs to be
configured properly so you need to add it to the list of configured providers,
all of which implement [AuthProviderConfig](#AuthProviderConfig):
```ts
export const providers = [
{
provider: 'providerA', # used as an identifier
options: { ... }, # consult the provider documentation for which options you should provide
disableRefresh: true # if the provider lacks refresh tokens
},
```
**`plugins/auth-backend/src/providers/factories.ts`** When the `auth-backend`
starts it sets up routing for all the available providers by calling
`createAuthProviderRouter` on each provider. You need to import the create
method from the provider and add it to the factory:
`createAuthProviderRouter` on each provider. You need to import the factory
function from the provider and add it to the factory:
```ts
import { createProviderAProvider } from './providerA';
const factories: { [providerId: string]: AuthProviderFactory } = {
providerA: createProviderAProvider,
};
@@ -203,10 +238,21 @@ web browser and you should be able to trigger the authorization flow.
```ts
export interface OAuthProviderHandlers {
start(req: express.Request, options: any): Promise<any>;
handler(req: express.Request): Promise<any>;
refresh?(refreshToken: string, scope: string): Promise<any>;
logout?(): Promise<any>;
start(
req: express.Request,
options: Record<string, string>,
): Promise<RedirectInfo>;
handler(
req: express.Request,
): Promise<{
response: AuthResponse<OAuthProviderInfo>;
refreshToken?: string;
}>;
refresh?(
refreshToken: string,
scope: string,
): Promise<AuthResponse<OAuthProviderInfo>>;
logout?(): Promise<void>;
}
```
@@ -221,12 +267,17 @@ export interface AuthProviderRouteHandlers {
}
```
##### AuthProviderConfig
##### AuthProviderFactory
```ts
export type AuthProviderConfig = {
provider: string;
options: any;
disableRefresh?: boolean;
export type AuthProviderFactoryOptions = {
globalConfig: AuthProviderConfig;
config: Config;
logger: Logger;
tokenIssuer: TokenIssuer;
};
export type AuthProviderFactory = (
options: AuthProviderFactoryOptions,
) => AuthProviderRouteHandlers;
```
+27 -22
View File
@@ -26,7 +26,7 @@ refer to the type documentation under
`plugins/auth-backend/src/providers/types.ts`.
There are currently two different classes for two authentication mechanisms that
implement this interface: an `OAuthProvider` for [OAuth](https://oauth.net/2/)
implement this interface: an `OAuthAdapter` for [OAuth](https://oauth.net/2/)
based mechanisms and a `SAMLAuthProvider` for
[SAML](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html)
based mechanisms.
@@ -50,11 +50,12 @@ OAuth2) providers, you can configure them by setting the right variables in
Each authentication provider (except SAML) needs five parameters: an OAuth
client ID, a client secret, an authorization endpoint and a token endpoint, and
an app origin. The app origin is the URL at which the frontend of the
application is hosted. This is required because the application opens a popup
window to perform the authentication, and once the flow is completed, the popup
window sends a `postMessage` to the frontend application to indicate the result
of the operation. Also this URL is used to verify that authentication requests
are coming from only this endpoint.
application is hosted, and it is read from the `app.baseUrl` config. This is
required because the application opens a popup window to perform the
authentication, and once the flow is completed, the popup window sends a
`postMessage` to the frontend application to indicate the result of the
operation. Also this URL is used to verify that authentication requests are
coming from only this endpoint.
These values are configured via the `app-config.yaml` present in the root of
your app folder.
@@ -64,8 +65,6 @@ auth:
providers:
google:
development:
appOrigin: "http://localhost:3000/"
secure: false
clientId:
$secret:
env: AUTH_GOOGLE_CLIENT_ID
@@ -74,8 +73,6 @@ auth:
env: AUTH_GOOGLE_CLIENT_SECRET
github:
development:
appOrigin: "http://localhost:3000/"
secure: false
clientId:
$secret:
env: AUTH_GITHUB_CLIENT_ID
@@ -87,8 +84,6 @@ auth:
env: AUTH_GITHUB_ENTERPRISE_INSTANCE_URL
gitlab:
development:
appOrigin: "http://localhost:3000/"
secure: false
clientId:
$secret:
...
@@ -96,24 +91,34 @@ auth:
## Technical Notes
### EnvironmentHandler
### OAuthEnvironmentHandler
The concept of an "env" is core to the way the auth backend works. It uses an
`env` query parameter to identify the environment in which the application is
running (`development`, `staging`, `production`, etc). Each runtime can support
multiple environments at the same time and the right handler for each request is
identified and dispatched to based on the `env` parameter. All
`AuthProviderRouteHandlers` are wrapped within an `EnvironmentHandler`.
`AuthProviderRouteHandlers` are wrapped within an `OAuthEnvironmentHandler`.
An `EnvironmentHandler` takes an ID for each provider that it wraps, the
handlers for each env the provider is supported in, and a function that, given a
`Request` as argument, can extract the information about the env under which it
should be processed.
To instantiate multiple OAuth providers for different environments, use
`OAuthEnvironmentHandler.mapConfig`. It's a helper to iterate over a
configuration object that is a map of environment to configurations. See one of
the existing OAuth providers for an example of how it is used.
Each provider exposes a factory function `createXProvider` (where X is the name
of the provider) that takes the global config, env and other parameters and
returns an `AuthProviderRouteHandlers` for each env, and an `envIdentifier`
function to identify the env of a request.
Given the following configuration:
```yaml
development:
clientId: abc
clientSecret: secret
production:
clientId: xyz
clientSecret: supersecret
```
The `OAuthEnvironmentHandler.mapConfig(config, envConfig => ...)` call will
split the `config` by the top level `development` and `production` keys, and
pass on each block as `envConfig`.
For a list of currently available providers, look in the `factories` module
located in `plugins/auth-backend/src/providers/factories.ts`
+2 -2
View File
@@ -116,8 +116,8 @@ components. If youd like to help build up our design system, you can also add
components weve designed to the Storybook as well.
**[Figma](https://www.figma.com/@backstage)** - we're stoked to be using Figma
Community to share our design assets. You can duplicate our UI Kit
and design your own plugin for Backstage.
Community to share our design assets. You can duplicate our UI Kit and design
your own plugin for Backstage.
**[Discord](https://discord.gg/EBHEGzX)** - all design questions should be
directed to the _#design_ channel.
+11 -10
View File
@@ -23,30 +23,31 @@ 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.
`Component`, `API` and `Location`. 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]
- allow: [Component, API, Location, Template]
locations:
- type: github
target: https://github.com/org/example/blob/master/org-data.yaml
allow: [Group]
rules:
- 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.
We are able to add entities of kind `Component`, `API`, `Location`, or
`Template` 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.
value, meaning that you need to add rules for the default kinds if you want
those to still be allowed.
The following configuration will reject any kind of entities from being added to
the catalog:
@@ -34,7 +34,7 @@ software catalog API.
"annotations": {
"backstage.io/managed-by-location": "file:/tmp/component-info.yaml",
"example.com/service-discovery": "artistweb",
"circleci.com/project-slug": "gh/example-org/artist-website"
"circleci.com/project-slug": "github/example-org/artist-website"
},
"description": "The place to be, for great artists",
"etag": "ZjU2MWRkZWUtMmMxZS00YTZiLWFmMWMtOTE1NGNiZDdlYzNk",
@@ -66,7 +66,7 @@ metadata:
system: public-websites
annotations:
example.com/service-discovery: artistweb
circleci.com/project-slug: gh/example-org/artist-website
circleci.com/project-slug: github/example-org/artist-website
tags:
- java
spec:
@@ -235,6 +235,9 @@ The `backstage.io/` prefix is reserved for use by Backstage core components.
Values can be of any length, but are limited to being strings.
There is a list of [well-known annotations](well-known-annotations.md), but
anybody is free to add more annotations as they see fit.
### `tags` [optional]
A list of single-valued strings, for example to classify catalog entities in
@@ -290,7 +293,7 @@ Exactly equal to `backstage.io/v1alpha1` and `Component`, respectively.
The type of component as a string, e.g. `website`. This field is required.
The software catalog accepts any type value, but an organisation should take
The software catalog accepts any type value, but an organization should take
great care to establish a proper taxonomy for these. Tools including Backstage
itself may read this field and behave differently depending on its value. For
example, a website type component may present tooling in the Backstage interface
@@ -304,9 +307,9 @@ The current set of well-known and common values for this field is:
### `spec.lifecycle` [required]
The lifecyle state of the component, e.g. `production`. This field is required.
The lifecycle state of the component, e.g. `production`. This field is required.
The software catalog accepts any lifecycle value, but an organisation should
The software catalog accepts any lifecycle value, but an organization should
take great care to establish a proper taxonomy for these.
The current set of well-known and common values for this field is:
@@ -418,7 +421,7 @@ potentially search and group templates by these tags.
The type of component as a string, e.g. `website`. This field is optional but
recommended.
The software catalog accepts any type value, but an organisation should take
The software catalog accepts any type value, but an organization should take
great care to establish a proper taxonomy for these. Tools including Backstage
itself may read this field and behave differently depending on its value. For
example, a website type component may present tooling in the Backstage interface
@@ -508,7 +511,7 @@ Exactly equal to `backstage.io/v1alpha1` and `API`, respectively.
The type of the API definition as a string, e.g. `openapi`. This field is
required.
The software catalog accepts any type value, but an organisation should take
The software catalog accepts any type value, but an organization should take
great care to establish a proper taxonomy for these. Tools including Backstage
itself may read this field and behave differently depending on its value. For
example, an OpenAPI type API may be displayed using an OpenAPI viewer tooling in
+2 -2
View File
@@ -1,7 +1,7 @@
---
id: software-catalog-overview
title: Backstage Service Catalog (alpha)
sidebar_label: Backstage Service Catalog
sidebar_label: Overview
---
## What is a Service Catalog?
@@ -48,7 +48,7 @@ There are 3 ways to add components to the catalog:
1. Manually register components
2. Creating new components through Backstage
3. Integrating with and [external source](external-integrations.md)
3. Integrating with an [external source](external-integrations.md)
### Manually register components
@@ -0,0 +1,134 @@
---
id: well-known-annotations
title: Well-known Annotations on Catalog Entities
sidebar_label: Well-known Annotations
---
This section lists a number of well known
[annotations](descriptor-format.md#annotations-optional), that have defined
semantics. They can be attached to catalog entities and consumed by plugins as
needed.
## Annotations
This is a (non-exhaustive) list of annotations that are known to be in active
use.
### backstage.io/managed-by-location
```yaml
# Example:
metadata:
annotations:
backstage.io/managed-by-location: github:http://github.com/spotify/backstage/catalog-info.yaml
```
The value of this annotation is a so called location reference string, that
points to the source from which the entity was originally fetched. This
annotation is added automatically by the catalog as it fetches the data from a
registered location, and is not meant to normally be written by humans. The
annotation may point to any type of generic location that the catalog supports,
so it cannot be relied on to always be specifically of type `github`, nor that
it even represents a single file. Note also that a single location can be the
source of many entities, so it represents a many-to-one relationship.
The format of the value is `<type>:<target>`. Note that the target may also
contain colons, so it is not advisable to naively split the value on `:` and
expecting a two-item array out of it. The format of the target part is
type-dependent and could conceivably even be an empty string, but the separator
colon is always present.
### backstage.io/techdocs-ref
```yaml
# Example:
metadata:
annotations:
backstage.io/techdocs-ref: github:https://github.com/spotify/backstage.git
```
The value of this annotation is a location reference string (see above). If this
annotation is specified, it is expected to point to a repository that the
TechDocs system can read and generate docs from.
### backstage.io/jenkins-github-folder
```yaml
# Example:
metadata:
annotations:
backstage.io/jenkins-github-folder: folder-name/job-name
```
The value of this annotation is the path to a job on Jenkins, that builds this
entity.
Specifying this annotation may enable Jenkins related features in Backstage for
that entity.
### github.com/project-slug
```yaml
# Example:
metadata:
annotations:
github.com/project-slug: spotify/backstage
```
The value of this annotation is the so-called slug that identifies a project on
[GitHub](https://github.com) that is related to this entity. It is on the format
`<organization>/<project>`, and is the same as can be seen in the URL location
bar of the browser when viewing that project.
Specifying this annotation will enable GitHub related features in Backstage for
that entity.
### sentry.io/project-slug
```yaml
# Example:
metadata:
annotations:
sentry.io/project-slug: pump-station
```
The value of this annotation is the so-called slug (or alternatively, the ID) of
a [Sentry](https://sentry.io) project within your organization. The organization
slug is currently not configurable on a per-entity basis, but is assumed to be
the same for all entities in the catalog.
Specifying this annotation may enable Sentry related features in Backstage for
that entity.
### rollbar.com/project-slug
```yaml
# Example:
metadata:
annotations:
rollbar.com/project-slug: spotify/pump-station
```
The value of this annotation is the so-called slug (or alternatively, the ID) of
a [Rollbar](https://rollbar.com) project within your organization. The value can
be the format of `[organization]/[project-slug]` or just `[project-slug]`. When
the organization slug is omitted the `app-config.yaml` will be used as a
fallback (`rollbar.organization` followed by `organization.name`).
Specifying this annotation may enable Rollbar related features in Backstage for
that entity.
## Deprecated Annotations
The following annotations are deprecated, and only listed here to aid in
migrating away from them.
### backstage.io/github-actions-id
This annotation was used for a while to enable the GitHub Actions feature. This
is now instead using the [github.com/project-slug](#github-com-project-slug)
annotation, with the same value format.
## Links
- [Descriptor Format: annotations](descriptor-format.md#annotations-optional)
@@ -58,6 +58,29 @@ 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.
You can add the template files to the catalog through
[static location configuration](../software-catalog/configuration.md#static-location-configuration),
for example
```yaml
catalog:
locations:
- type: github
target: https://github.com/spotify/cookiecutter-golang/blob/master/template.yaml
rules:
- allow: [Template]
```
Templates can also be added by posting the to the catalog directly. Note that if
you're doing this, you need to configure the catalog to allow template entities
to be ingested from any source, for example:
```yaml
catalog:
rules:
- allow: [Component, API, Template]
```
For loading from a file, the following command should work when the backend is
running:
@@ -7,18 +7,18 @@ 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 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.
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.
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,
but PR's are always welcome.
`@spotify/plugin-scaffolder-backend`. Currently, only one publisher is
supported, but PR's are always welcome.
An full example backend can be found
[here](https://github.com/spotify/backstage/blob/d91c10f654475a60829fa33a5c81018e517a319a/packages/backend/src/plugins/scaffolder.ts),
+3 -2
View File
@@ -1,10 +1,11 @@
---
id: software-templates-index
title: Software Templates
title: Backstage Software Templates
sidebar_label: Overview
---
The Software Templates part of Backstage is a tool that can help you create
Components inside Backstage. By default, it has the ability to load skeletons of
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.
@@ -30,9 +30,9 @@ 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
!!! 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
+16 -5
View File
@@ -28,7 +28,7 @@ installed:
To create a new Backstage application for TechDocs, run the following command:
```bash
npx @backstage/cli create-app
npx @backstage/create-app
```
You will then be prompted to enter a name for your application. Once that's
@@ -74,17 +74,28 @@ export { plugin as TechDocs } from '@backstage/plugin-techdocs';
### Setting the configuration
TechDocs allows for configuration of the docs storage URL through your
`app-config` file.
`app-config` file. We provide two different values to be configured,
`requestUrl` and `storageUrl`. The `requestUrl` is what the reader will request
its data from, and `storageUrl` is where the backend can find the stored
documentation.
The default storage URL:
The default storage and request URLs:
```yaml
techdocs:
storageUrl: http://localhost:7000/techdocs/static/docs
requestUrl: http://localhost:7000/techdocs/docs
```
If you want to configure this to point to another storage URL, change the value
of `storageUrl`.
If you want `techdocs-backend` to manage building and publishing you want
`requestUrl` to point to the default value (or wherever `techdocs-backend` is
hosted). `storageUrl` should be where your publisher publishes your docs. Using
the default `LocalPublish` that is the default value.
If you have a setup where you are not using `techdocs-backend` for managing
building and publishing of your documentation you want to change the
`requestUrl` to point to your storage. In this case `storageUrl` is not
required.
## Run Backstage locally
@@ -3,4 +3,34 @@ id: configure-app-with-plugins
title: Configuring App with plugins
---
## Adding existing plugins to your app
Coming soon!
### Adding a plugin page to the Sidebar
In a standard Backstage app created with
[@backstage/create-app](./create-an-app.md), the sidebar is managed inside
`packages/app/src/sidebar.tsx`. The file exports the entire `Sidebar` element of
your app, which you can extend with additional entries by adding new
`SidebarItem` elements.
For example, if you install the `api-docs` plugin, a matching `SidebarItem`
could be something like this:
```tsx
// Import icon from MUI
import ExtensionIcon from '@material-ui/icons/Extension';
// ... inside the AppSidebar component
<SidebarItem icon={ExtensionIcon} to="api-docs" text="APIs" />;
```
You can also use your own SVGs directly as icon components. Just make sure they
are sized according to the Material UI's
[SvgIcon](https://material-ui.com/api/svg-icon/) default of 24x24px, and set the
extension to `.icon.svg`. For example:
```ts
import InternalToolIcon from './internal-tool.icon.svg';
```
+17 -2
View File
@@ -15,7 +15,13 @@ To create a Backstage app, you will need to have
[NodeJS](https://nodejs.org/en/download/) Active LTS Release installed
(currently v12).
With `npx`:
Backstage provides a utility for creating new apps. It guides you through the
initial setup of selecting the name of the app and a database for the backend.
The database options are either SQLite or PostgreSQL, where the latter requires
you to set up a separate database instance. If in doubt, choose SQLite, but
don't worry about the choice, it's easy to change later!
The easiest way to run the create app package is with `npx`:
```bash
npx @backstage/create-app
@@ -25,7 +31,7 @@ This will create a new Backstage App inside the current folder. The name of the
app-folder is the name that was provided when prompted.
<p align='center'>
<img src='https://github.com/spotify/backstage/raw/master/docs/getting-started/create-app_output.png' width='600' alt='create app'>
<img src='../assets/getting-started/create-app_output.png' width='600' alt='create app'>
</p>
Inside that directory, it will generate all the files and folder structure
@@ -80,3 +86,12 @@ yarn start
_When `yarn start` is ready it should open up a browser window displaying your
app, if not you can navigate to `http://localhost:3000`._
In most cases you will want to start the backend as well, as it is required for
the catalog to work, along with many other plugins.
To start the backend, open a separate terminal session and run the following:
```bash
yarn --cwd packages/backend start
```
+6 -7
View File
@@ -10,21 +10,20 @@ title: Other
Run the following commands if you have Docker environment
```bash
$ yarn install
$ yarn docker-build
$ docker run --rm -it -p 80:80 spotify/backstage
$ docker run --rm -it -p 7000:7000 -e NODE_ENV=development example-backend:latest
```
Then open http://localhost/ on your browser.
### Running with `docker-compose`
Run the following commands if you have docker and docker-compose for a full
example, with the example backend also deployed.
There is also a `docker-compose.yaml` that you can use to replace the previous
`docker run` command:
```bash
$ yarn docker-build:all
$ yarn install
$ yarn docker-build
$ docker-compose up
```
Then open http://localhost:3000 on your browser to see the example app with an
example backend.
@@ -65,6 +65,7 @@ yarn storybook # Start local storybook, useful for working on components in @bac
yarn workspace @backstage/plugin-welcome start # Serve welcome plugin only, also supports --check
yarn tsc # Run typecheck, use --watch for watch mode
yarn tsc:full # Run full type checking, for example without skipLibCheck, use in CI
yarn build # Build published versions of packages, depends on tsc
+2 -1
View File
@@ -4,7 +4,8 @@ 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.
This is made easy with a version manager such as nvm which allows for version
switching.
```bash
# Checking your version
+5 -7
View File
@@ -171,21 +171,19 @@ The frontend container can be built with a provided command.
```bash
yarn install
yarn tsc
yarn build
yarn run docker-build
yarn run docker-build:app
```
Running this will simply generate a Docker container containing the contents of
the UIs `dist` directory. The resulting container will be about 50MB in size.
the UIs `dist` directory.
The backend container can be built by running the following command in the
`packages/backend` directory.
The backend container can be built by running the following command:
```bash
yarn run build-image
yarn run docker-build
```
This will create a ~500MB container called `example-backend`.
This will create a container called `example-backend`.
The lighthouse-audit-service container is already publicly available in Docker
Hub and can be downloaded and ran with
+15 -6
View File
@@ -79,18 +79,26 @@ guidelines to get started.
- Further improvements to platform documentation
### Plugins
Building and maintaining [plugins](https://backstage.io/plugins) is the work of
the entire Backstage community.
A list of plugins that are in development is
[available here](https://github.com/spotify/backstage/issues?q=is%3Aissue+is%3Aopen+label%3Aplugin+sort%3Areactions-%2B1-desc).
We strongly recommend to upvote 👍 plugins you are interested in. This helps us
and the community prioritize what plugins to build.
Are you missing a plugin for your favorite tool? Please
[suggest a new one](https://github.com/spotify/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME).
Chances are that someone will jump in and help build it.
### Future work 🔮
- **[Backstage platform is stable](https://github.com/spotify/backstage/milestone/19)** -
The platform APIs and features are stable and can be depended on for
production use. After this plugins will require little to no maintenance.
- **[Plugin marketplace](https://github.com/spotify/backstage/issues/2009)** -
As the ecosystem of Backstage plugins continues to grow it is becoming
increasingly hard to keep track of what plugins are available. To solve this
we imagine a "Plugin marketplace" that helps with discovery and installation
of plugins.
- **Deploy a product demo at `demo.backstage.io`** - Deploy a typical Backstage
deployment available publicly so that people can click around and get a feel
for the product without having to install anything.
@@ -111,6 +119,7 @@ guidelines to get started.
### Completed milestones ✅
- [Plugin marketplace](https://backstage.io/plugins)
- [Improved and move documentation to backstage.io](https://backstage.io/docs/overview/what-is-backstage)
- [Backstage Service Catalog (alpha)](https://backstage.io/blog/2020/06/22/backstage-service-catalog-alpha)
- [Backstage Software Templates (alpha)](https://backstage.io/blog/2020/08/05/announcing-backstage-software-templates)
+3 -1
View File
@@ -6,7 +6,9 @@ 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:
create a file in
[`microsite/data/plugins`](https://github.com/spotify/backstage/tree/master/microsite/data/plugins)
with your plugin's information. Example:
```yaml
---
+8 -1
View File
@@ -21,8 +21,15 @@ To create a plugin, follow the steps outlined [here](create-a-plugin.md).
If you start developing a plugin that you aim to release as open source, we
suggest that you create a new
[new Issue](https://github.com/spotify/backstage/issues/new?template=plugin_template.md).
[new Issue](https://github.com/spotify/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME).
This helps the community know what plugins are in development.
You can also use this process if you have an idea for a good plugin but you hope
that someone else will pick up the work.
## Integrate into the Service Catalog
If your plugin isn't supposed to live as a standalone page, but rather needs to
be presented as a part of a Service Catalog (e.g. a separate tab or a card on an
"Overview" tab), then check out
[the instruction](integrating-plugin-into-service-catalog.md). on how to do it.
@@ -0,0 +1,124 @@
---
id: integrating-plugin-into-service-catalog
title: Integrate into the Service Catalog
---
> This is an advanced use case and currently is an experimental feature. Expect
> API to change over time
## Steps
1. [Create a plugin](#create-a-plugin)
1. [Export a router with relative routes](#export-a-router)
1. [Import and use router in the APP](#import-and-use-router-in-the-app)
### Create a plugin
Follow the [same process](create-a-plugin.md) as for standalone plugin. You
should have a separate package in a folder, which represents your plugin.
Example:
```
$ yarn create-plugin
> ? Enter an ID for the plugin [required] my-plugin
> ? Enter the owner(s) of the plugin. If specified, this will be added to CODEOWNERS for the plugin path. [optional]
Creating the plugin...
```
### Export a router
Now in the plugin you have a `Router.tsx` file in the `src` folder. By default
it contains only one example route. Create a routing structure needed for your
plugin, keeping in mind that the whole set of routes defined here are going to
be mounted under some different route in the App.
Example:
`my-plugin` consists of 2 different views - `/me` and `/about`. I envision
people integrating it into plugin catalog as a tab named "MyPlugin". Then, my
`Routes.tsx` for the plugin is going to look like:
```tsx
<Routes>
<Route path="/me" element={<MePage />} />
<Route path="/about" element={<AboutPage />} />
</Routes>
```
(where MePage and AboutPage are 2 components defined in your plugin and imported
accordingly inside `Router.tsx`)
> Pay attention, if your `MePage` references the `AboutPage` it needs to do it
> through link to `about`, not `/about`. This allows react-router v6 to enable
> its relative routing mechanism. Read more -
> https://reacttraining.com/blog/react-router-v6-pre/#relative-route-path-and-link-to
### Import and use router in the APP
In the `app/src/components/catalog/EntityPage.tsx` (app === your folder,
containing backstage app) import your created Router:
```tsx
import { Router as MyPluginRouter } from '@backstage/plugin-my-plugin;
```
Now, you need to mount `MyPluginRouter` onto some route, for example if you had:
```tsx
const DefaultEntityPage = ({ entity }: { entity: Entity }) => (
<EntityPageLayout>
<EntityPageLayout.Content
path="/"
title="Overview"
element={<OverviewPage entity={entity} />}
/>
</EntityPageLayout>
);
```
after you add your code it becomes:
```tsx
const DefaultEntityPage = ({ entity }: { entity: Entity }) => (
<EntityPageLayout>
<EntityPageLayout.Content
path="/"
title="Overview"
element={<OverviewPage entity={entity} />}
/>
<EntityPageLayout.Content
path="/my-plugin"
title="My Plugin"
element={<MyPluginRouter entity={entity} />}
/>
</EntityPageLayout>
);
```
All of magic happens thanks to the `EntityPageLayout` component, which comes as
an export from `@backstage/plugin-catalog` package.
```tsx
type EntityPageLayoutContentProps = {
/**
* Going to be transformed into react-router v6
* path under the hood. Read more at https://reacttraining.com/blog/react-router-v6-pre
*/
path: string;
/**
* Gets transformed into the title for the tab
*/
title: string;
/**
* Element that is rendered when the location
* matches the path provided
*/
element: JSX.Element;
};
```
> You can either pass the entity from App to the plugin's router as a prop or
> use `useEntity` hook from `@backstage/plugin-catalog` directly inside your
> plugin.
+14
View File
@@ -39,4 +39,18 @@ $ git push origin -u new-release
And then create a PR. Once the PR is approved and merged into master, the master
build will publish new versions of all bumped packages.
### Include new changes in existing release PR
If you want to include some last minute changes to an existing release PR,
follow these instructions:
```sh
$ git checkout master
$ git pull
$ git checkout new-release
$ git reset --hard master
$ yarn release
$ git push --force
```
[Back to Docs](../README.md)
+5 -5
View File
@@ -1,7 +1,7 @@
# AlertApi
The AlertApi type is defined at
[packages/core-api/src/apis/definitions/AlertApi.ts:29](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/AlertApi.ts#L29).
[packages/core-api/src/apis/definitions/AlertApi.ts:29](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/AlertApi.ts#L29).
The following Utility API implements this type: [alertApiRef](./README.md#alert)
@@ -38,7 +38,7 @@ export type AlertMessage = {
</pre>
Defined at
[packages/core-api/src/apis/definitions/AlertApi.ts:19](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/AlertApi.ts#L19).
[packages/core-api/src/apis/definitions/AlertApi.ts:19](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/AlertApi.ts#L19).
Referenced by: [post](#post), [alert\$](#alert).
@@ -67,7 +67,7 @@ export type Observable&lt;T&gt; = {
</pre>
Defined at
[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L53).
[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L53).
Referenced by: [alert\$](#alert).
@@ -86,7 +86,7 @@ export type Observer&lt;T&gt; = {
</pre>
Defined at
[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L24).
[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L24).
Referenced by: [Observable](#observable).
@@ -109,6 +109,6 @@ export type Subscription = {
</pre>
Defined at
[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L33).
[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L33).
Referenced by: [Observable](#observable).
+8 -8
View File
@@ -1,7 +1,7 @@
# AppThemeApi
The AppThemeApi type is defined at
[packages/core-api/src/apis/definitions/AppThemeApi.ts:50](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/AppThemeApi.ts#L50).
[packages/core-api/src/apis/definitions/AppThemeApi.ts:50](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/AppThemeApi.ts#L50).
The following Utility API implements this type:
[appThemeApiRef](./README.md#apptheme)
@@ -76,7 +76,7 @@ export type AppTheme = {
</pre>
Defined at
[packages/core-api/src/apis/definitions/AppThemeApi.ts:24](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/AppThemeApi.ts#L24).
[packages/core-api/src/apis/definitions/AppThemeApi.ts:24](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/AppThemeApi.ts#L24).
Referenced by: [getInstalledThemes](#getinstalledthemes).
@@ -87,7 +87,7 @@ export type BackstagePalette = Palette &amp; <a href="#paletteadditions">Palette
</pre>
Defined at
[packages/theme/src/types.ts:67](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/theme/src/types.ts#L67).
[packages/theme/src/types.ts:70](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/theme/src/types.ts#L70).
Referenced by: [BackstageTheme](#backstagetheme).
@@ -100,7 +100,7 @@ export interface BackstageTheme extends Theme {
</pre>
Defined at
[packages/theme/src/types.ts:70](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/theme/src/types.ts#L70).
[packages/theme/src/types.ts:73](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/theme/src/types.ts#L73).
Referenced by: [AppTheme](#apptheme).
@@ -129,7 +129,7 @@ export type Observable&lt;T&gt; = {
</pre>
Defined at
[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L53).
[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L53).
Referenced by: [activeThemeId\$](#activethemeid).
@@ -148,7 +148,7 @@ export type Observer&lt;T&gt; = {
</pre>
Defined at
[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L24).
[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L24).
Referenced by: [Observable](#observable).
@@ -204,7 +204,7 @@ type PaletteAdditions = {
</pre>
Defined at
[packages/theme/src/types.ts:23](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/theme/src/types.ts#L23).
[packages/theme/src/types.ts:23](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/theme/src/types.ts#L23).
Referenced by: [BackstagePalette](#backstagepalette).
@@ -227,6 +227,6 @@ export type Subscription = {
</pre>
Defined at
[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L33).
[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L33).
Referenced by: [Observable](#observable).
@@ -1,16 +1,20 @@
# BackstageIdentityApi
The BackstageIdentityApi type is defined at
[packages/core-api/src/apis/definitions/auth.ts:144](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L144).
[packages/core-api/src/apis/definitions/auth.ts:144](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L144).
The following Utility APIs implement this type:
- [auth0AuthApiRef](./README.md#auth0auth)
- [githubAuthApiRef](./README.md#githubauth)
- [gitlabAuthApiRef](./README.md#gitlabauth)
- [googleAuthApiRef](./README.md#googleauth)
- [microsoftAuthApiRef](./README.md#microsoftauth)
- [oktaAuthApiRef](./README.md#oktaauth)
## Members
@@ -62,7 +66,7 @@ export type AuthRequestOptions = {
</pre>
Defined at
[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L40).
[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L40).
Referenced by: [getBackstageIdentity](#getbackstageidentity).
@@ -83,6 +87,6 @@ export type BackstageIdentity = {
</pre>
Defined at
[packages/core-api/src/apis/definitions/auth.ts:157](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L157).
[packages/core-api/src/apis/definitions/auth.ts:157](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L157).
Referenced by: [getBackstageIdentity](#getbackstageidentity).
+17 -9
View File
@@ -1,13 +1,19 @@
# Config
The Config type is defined at
[packages/config/src/types.ts:32](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/config/src/types.ts#L32).
[packages/config/src/types.ts:32](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/config/src/types.ts#L32).
The following Utility API implements this type:
[configApiRef](./README.md#config)
## Members
### has()
<pre>
has(key: string): boolean
</pre>
### keys()
<pre>
@@ -17,13 +23,13 @@ keys(): string[]
### get()
<pre>
get(key: string): <a href="#jsonvalue">JsonValue</a>
get(key?: string): <a href="#jsonvalue">JsonValue</a>
</pre>
### getOptional()
<pre>
getOptional(key: string): <a href="#jsonvalue">JsonValue</a> | undefined
getOptional(key?: string): <a href="#jsonvalue">JsonValue</a> | undefined
</pre>
### getConfig()
@@ -106,10 +112,12 @@ These types are part of the API declaration, but may not be unique to this API.
<pre>
export type Config = {
has(key: string): boolean;
keys(): string[];
get(key: string): <a href="#jsonvalue">JsonValue</a>;
getOptional(key: string): <a href="#jsonvalue">JsonValue</a> | undefined;
get(key?: string): <a href="#jsonvalue">JsonValue</a>;
getOptional(key?: string): <a href="#jsonvalue">JsonValue</a> | undefined;
getConfig(key: string): Config;
getOptionalConfig(key: string): <a href="#config">Config</a> | undefined;
@@ -132,7 +140,7 @@ export type Config = {
</pre>
Defined at
[packages/config/src/types.ts:32](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/config/src/types.ts#L32).
[packages/config/src/types.ts:32](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/config/src/types.ts#L32).
Referenced by: [getConfig](#getconfig), [getOptionalConfig](#getoptionalconfig),
[getConfigArray](#getconfigarray),
@@ -145,7 +153,7 @@ export type JsonArray = <a href="#jsonvalue">JsonValue</a>[]
</pre>
Defined at
[packages/config/src/types.ts:18](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/config/src/types.ts#L18).
[packages/config/src/types.ts:18](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/config/src/types.ts#L18).
Referenced by: [JsonValue](#jsonvalue).
@@ -156,7 +164,7 @@ export type JsonObject = { [key in string]?: <a href="#jsonvalue">JsonValue</a>
</pre>
Defined at
[packages/config/src/types.ts:17](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/config/src/types.ts#L17).
[packages/config/src/types.ts:17](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/config/src/types.ts#L17).
Referenced by: [JsonValue](#jsonvalue).
@@ -173,7 +181,7 @@ export type JsonValue =
</pre>
Defined at
[packages/config/src/types.ts:19](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/config/src/types.ts#L19).
[packages/config/src/types.ts:19](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/config/src/types.ts#L19).
Referenced by: [get](#get), [getOptional](#getoptional),
[JsonObject](#jsonobject), [JsonArray](#jsonarray), [Config](#config).
@@ -0,0 +1,24 @@
# DiscoveryApi
The DiscoveryApi type is defined at
[packages/core-api/src/apis/definitions/DiscoveryApi.ts:30](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/DiscoveryApi.ts#L30).
The following Utility API implements this type:
[discoveryApiRef](./README.md#discovery)
## Members
### getBaseUrl()
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`
<pre>
getBaseUrl(pluginId: string): Promise&lt;string&gt;
</pre>
+6 -6
View File
@@ -1,7 +1,7 @@
# ErrorApi
The ErrorApi type is defined at
[packages/core-api/src/apis/definitions/ErrorApi.ts:53](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/ErrorApi.ts#L53).
[packages/core-api/src/apis/definitions/ErrorApi.ts:53](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/ErrorApi.ts#L53).
The following Utility API implements this type: [errorApiRef](./README.md#error)
@@ -41,7 +41,7 @@ type Error = {
</pre>
Defined at
[packages/core-api/src/apis/definitions/ErrorApi.ts:24](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/ErrorApi.ts#L24).
[packages/core-api/src/apis/definitions/ErrorApi.ts:24](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/ErrorApi.ts#L24).
Referenced by: [post](#post), [error\$](#error).
@@ -58,7 +58,7 @@ export type ErrorContext = {
</pre>
Defined at
[packages/core-api/src/apis/definitions/ErrorApi.ts:33](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/ErrorApi.ts#L33).
[packages/core-api/src/apis/definitions/ErrorApi.ts:33](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/ErrorApi.ts#L33).
Referenced by: [post](#post), [error\$](#error).
@@ -87,7 +87,7 @@ export type Observable&lt;T&gt; = {
</pre>
Defined at
[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L53).
[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L53).
Referenced by: [error\$](#error).
@@ -106,7 +106,7 @@ export type Observer&lt;T&gt; = {
</pre>
Defined at
[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L24).
[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L24).
Referenced by: [Observable](#observable).
@@ -129,6 +129,6 @@ export type Subscription = {
</pre>
Defined at
[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L33).
[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L33).
Referenced by: [Observable](#observable).
@@ -1,7 +1,7 @@
# FeatureFlagsApi
The FeatureFlagsApi type is defined at
[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:41](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L41).
[packages/core-api/src/apis/definitions/FeatureFlagsApi.ts:41](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L41).
The following Utility API implements this type:
[featureFlagsApiRef](./README.md#featureflags)
+2 -2
View File
@@ -1,7 +1,7 @@
# IdentityApi
The IdentityApi type is defined at
[packages/core-api/src/apis/definitions/IdentityApi.ts:22](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/IdentityApi.ts#L22).
[packages/core-api/src/apis/definitions/IdentityApi.ts:22](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/IdentityApi.ts#L22).
The following Utility API implements this type:
[identityApiRef](./README.md#identity)
@@ -76,6 +76,6 @@ export type ProfileInfo = {
</pre>
Defined at
[packages/core-api/src/apis/definitions/auth.ts:172](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L172).
[packages/core-api/src/apis/definitions/auth.ts:172](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L172).
Referenced by: [getProfile](#getprofile).
+5 -3
View File
@@ -1,7 +1,7 @@
# OAuthApi
The OAuthApi type is defined at
[packages/core-api/src/apis/definitions/auth.ts:67](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L67).
[packages/core-api/src/apis/definitions/auth.ts:67](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L67).
The following Utility APIs implement this type:
@@ -11,6 +11,8 @@ The following Utility APIs implement this type:
- [googleAuthApiRef](./README.md#googleauth)
- [microsoftAuthApiRef](./README.md#microsoftauth)
- [oauth2ApiRef](./README.md#oauth2)
- [oktaAuthApiRef](./README.md#oktaauth)
@@ -88,7 +90,7 @@ export type AuthRequestOptions = {
</pre>
Defined at
[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L40).
[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L40).
Referenced by: [getAccessToken](#getaccesstoken).
@@ -114,6 +116,6 @@ export type OAuthScope = string | string[]
</pre>
Defined at
[packages/core-api/src/apis/definitions/auth.ts:38](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L38).
[packages/core-api/src/apis/definitions/auth.ts:38](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L38).
Referenced by: [getAccessToken](#getaccesstoken).
@@ -1,7 +1,7 @@
# OAuthRequestApi
The OAuthRequestApi type is defined at
[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:99](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L99).
[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:99](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L99).
The following Utility API implements this type:
[oauthRequestApiRef](./README.md#oauthrequest)
@@ -72,7 +72,7 @@ export type AuthProvider = {
</pre>
Defined at
[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:27](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L27).
[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:27](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L27).
Referenced by: [AuthRequesterOptions](#authrequesteroptions),
[PendingAuthRequest](#pendingauthrequest).
@@ -96,7 +96,7 @@ export type AuthRequester&lt;AuthResponse&gt; = (
</pre>
Defined at
[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:66](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L66).
[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:66](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L66).
Referenced by: [createAuthRequester](#createauthrequester).
@@ -121,7 +121,7 @@ export type AuthRequesterOptions&lt;AuthResponse&gt; = {
</pre>
Defined at
[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:43](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L43).
[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:43](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L43).
Referenced by: [createAuthRequester](#createauthrequester).
@@ -150,7 +150,7 @@ export type Observable&lt;T&gt; = {
</pre>
Defined at
[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L53).
[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L53).
Referenced by: [authRequest\$](#authrequest).
@@ -169,7 +169,7 @@ export type Observer&lt;T&gt; = {
</pre>
Defined at
[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L24).
[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L24).
Referenced by: [Observable](#observable).
@@ -204,7 +204,7 @@ export type PendingAuthRequest = {
</pre>
Defined at
[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:77](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L77).
[packages/core-api/src/apis/definitions/OAuthRequestApi.ts:77](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L77).
Referenced by: [authRequest\$](#authrequest).
@@ -227,6 +227,6 @@ export type Subscription = {
</pre>
Defined at
[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L33).
[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L33).
Referenced by: [Observable](#observable).
@@ -1,12 +1,16 @@
# OpenIdConnectApi
The OpenIdConnectApi type is defined at
[packages/core-api/src/apis/definitions/auth.ts:104](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L104).
[packages/core-api/src/apis/definitions/auth.ts:104](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L104).
The following Utility APIs implement this type:
- [auth0AuthApiRef](./README.md#auth0auth)
- [googleAuthApiRef](./README.md#googleauth)
- [microsoftAuthApiRef](./README.md#microsoftauth)
- [oauth2ApiRef](./README.md#oauth2)
- [oktaAuthApiRef](./README.md#oktaauth)
@@ -70,6 +74,6 @@ export type AuthRequestOptions = {
</pre>
Defined at
[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L40).
[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L40).
Referenced by: [getIdToken](#getidtoken).
@@ -1,16 +1,20 @@
# ProfileInfoApi
The ProfileInfoApi type is defined at
[packages/core-api/src/apis/definitions/auth.ts:127](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L127).
[packages/core-api/src/apis/definitions/auth.ts:127](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L127).
The following Utility APIs implement this type:
- [auth0AuthApiRef](./README.md#auth0auth)
- [githubAuthApiRef](./README.md#githubauth)
- [gitlabAuthApiRef](./README.md#gitlabauth)
- [googleAuthApiRef](./README.md#googleauth)
- [microsoftAuthApiRef](./README.md#microsoftauth)
- [oauth2ApiRef](./README.md#oauth2)
- [oktaAuthApiRef](./README.md#oktaauth)
@@ -61,7 +65,7 @@ export type AuthRequestOptions = {
</pre>
Defined at
[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L40).
[packages/core-api/src/apis/definitions/auth.ts:40](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L40).
Referenced by: [getProfile](#getprofile).
@@ -89,6 +93,6 @@ export type ProfileInfo = {
</pre>
Defined at
[packages/core-api/src/apis/definitions/auth.ts:172](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L172).
[packages/core-api/src/apis/definitions/auth.ts:172](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L172).
Referenced by: [getProfile](#getprofile).
+61 -30
View File
@@ -1,59 +1,77 @@
---
id: README
title: Utility API References
---
# Backstage Core Utility APIs
The following is a list of all Utility APIs defined by `@backstage/core`. They
are available to use by plugins and components, and can be accessed using the
`useApi` hook, also provided by `@backstage/core`. For more information, see
https://github.com/spotify/backstage/blob/master/docs/api/utility-apis.md.
## alert
### alert
Used to report alerts and forward them to the app
Implemented type: [AlertApi](./AlertApi.md)
ApiRef:
[alertApiRef](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/AlertApi.ts#L41)
[alertApiRef](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/AlertApi.ts#L41)
## appTheme
### appTheme
API Used to configure the app theme, and enumerate options
Implemented type: [AppThemeApi](./AppThemeApi.md)
ApiRef:
[appThemeApiRef](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/AppThemeApi.ts#L74)
[appThemeApiRef](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/AppThemeApi.ts#L74)
## config
### auth0Auth
Provides authentication towards Auth0 APIs
Implemented types: [OpenIdConnectApi](./OpenIdConnectApi.md),
[ProfileInfoApi](./ProfileInfoApi.md),
[BackstageIdentityApi](./BackstageIdentityApi.md),
[SessionStateApi](./SessionStateApi.md)
ApiRef:
[auth0AuthApiRef](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L273)
### config
Used to access runtime configuration
Implemented type: [Config](./Config.md)
ApiRef:
[configApiRef](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/ConfigApi.ts#L22)
[configApiRef](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/ConfigApi.ts#L22)
## error
### discovery
Provides service discovery of backend plugins
Implemented type: [DiscoveryApi](./DiscoveryApi.md)
ApiRef:
[discoveryApiRef](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/DiscoveryApi.ts#L44)
### error
Used to report errors and forward them to the app
Implemented type: [ErrorApi](./ErrorApi.md)
ApiRef:
[errorApiRef](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/ErrorApi.ts#L65)
[errorApiRef](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/ErrorApi.ts#L65)
## featureFlags
### featureFlags
Used to toggle functionality in features across Backstage
Implemented type: [FeatureFlagsApi](./FeatureFlagsApi.md)
ApiRef:
[featureFlagsApiRef](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L58)
[featureFlagsApiRef](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/FeatureFlagsApi.ts#L58)
## githubAuth
### githubAuth
Provides authentication towards GitHub APIs
@@ -63,9 +81,9 @@ Implemented types: [OAuthApi](./OAuthApi.md),
[SessionStateApi](./SessionStateApi.md)
ApiRef:
[githubAuthApiRef](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L230)
[githubAuthApiRef](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L230)
## gitlabAuth
### gitlabAuth
Provides authentication towards GitLab APIs
@@ -75,9 +93,9 @@ Implemented types: [OAuthApi](./OAuthApi.md),
[SessionStateApi](./SessionStateApi.md)
ApiRef:
[gitlabAuthApiRef](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L260)
[gitlabAuthApiRef](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L260)
## googleAuth
### googleAuth
Provides authentication towards Google APIs and identities
@@ -88,18 +106,31 @@ Implemented types: [OAuthApi](./OAuthApi.md),
[SessionStateApi](./SessionStateApi.md)
ApiRef:
[googleAuthApiRef](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L213)
[googleAuthApiRef](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L213)
## identity
### identity
Provides access to the identity of the signed in user
Implemented type: [IdentityApi](./IdentityApi.md)
ApiRef:
[identityApiRef](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/IdentityApi.ts#L54)
[identityApiRef](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/IdentityApi.ts#L54)
## oauth2
### microsoftAuth
Provides authentication towards Microsoft APIs and identities
Implemented types: [OAuthApi](./OAuthApi.md),
[OpenIdConnectApi](./OpenIdConnectApi.md),
[ProfileInfoApi](./ProfileInfoApi.md),
[BackstageIdentityApi](./BackstageIdentityApi.md),
[SessionStateApi](./SessionStateApi.md)
ApiRef:
[microsoftAuthApiRef](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L287)
### oauth2
Example of how to use oauth2 custom provider
@@ -108,18 +139,18 @@ Implemented types: [OAuthApi](./OAuthApi.md),
[ProfileInfoApi](./ProfileInfoApi.md), [SessionStateApi](./SessionStateApi.md)
ApiRef:
[oauth2ApiRef](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L270)
[oauth2ApiRef](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L301)
## oauthRequest
### oauthRequest
An API for implementing unified OAuth flows in Backstage
Implemented type: [OAuthRequestApi](./OAuthRequestApi.md)
ApiRef:
[oauthRequestApiRef](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L130)
[oauthRequestApiRef](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/OAuthRequestApi.ts#L130)
## oktaAuth
### oktaAuth
Provides authentication towards Okta APIs
@@ -130,13 +161,13 @@ Implemented types: [OAuthApi](./OAuthApi.md),
[SessionStateApi](./SessionStateApi.md)
ApiRef:
[oktaAuthApiRef](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L243)
[oktaAuthApiRef](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L243)
## storage
### storage
Provides the ability to store data which is unique to the user
Implemented type: [StorageApi](./StorageApi.md)
ApiRef:
[storageApiRef](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/StorageApi.ts#L68)
[storageApiRef](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/StorageApi.ts#L68)
@@ -1,16 +1,20 @@
# SessionStateApi
The SessionStateApi type is defined at
[packages/core-api/src/apis/definitions/auth.ts:201](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L201).
[packages/core-api/src/apis/definitions/auth.ts:201](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L201).
The following Utility APIs implement this type:
- [auth0AuthApiRef](./README.md#auth0auth)
- [githubAuthApiRef](./README.md#githubauth)
- [gitlabAuthApiRef](./README.md#gitlabauth)
- [googleAuthApiRef](./README.md#googleauth)
- [microsoftAuthApiRef](./README.md#microsoftauth)
- [oauth2ApiRef](./README.md#oauth2)
- [oktaAuthApiRef](./README.md#oktaauth)
@@ -52,7 +56,7 @@ export type Observable&lt;T&gt; = {
</pre>
Defined at
[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L53).
[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L53).
Referenced by: [sessionState\$](#sessionstate).
@@ -71,7 +75,7 @@ export type Observer&lt;T&gt; = {
</pre>
Defined at
[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L24).
[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L24).
Referenced by: [Observable](#observable).
@@ -87,7 +91,7 @@ export enum SessionState {
</pre>
Defined at
[packages/core-api/src/apis/definitions/auth.ts:192](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/auth.ts#L192).
[packages/core-api/src/apis/definitions/auth.ts:192](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/auth.ts#L192).
Referenced by: [sessionState\$](#sessionstate).
@@ -110,6 +114,6 @@ export type Subscription = {
</pre>
Defined at
[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L33).
[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L33).
Referenced by: [Observable](#observable).
+6 -6
View File
@@ -1,7 +1,7 @@
# StorageApi
The StorageApi type is defined at
[packages/core-api/src/apis/definitions/StorageApi.ts:31](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/StorageApi.ts#L31).
[packages/core-api/src/apis/definitions/StorageApi.ts:31](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/StorageApi.ts#L31).
The following Utility API implements this type:
[storageApiRef](./README.md#storage)
@@ -79,7 +79,7 @@ export type Observable&lt;T&gt; = {
</pre>
Defined at
[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L53).
[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L53).
Referenced by: [observe\$](#observe), [StorageApi](#storageapi).
@@ -98,7 +98,7 @@ export type Observer&lt;T&gt; = {
</pre>
Defined at
[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L24).
[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L24).
Referenced by: [Observable](#observable).
@@ -144,7 +144,7 @@ export interface StorageApi {
</pre>
Defined at
[packages/core-api/src/apis/definitions/StorageApi.ts:31](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/StorageApi.ts#L31).
[packages/core-api/src/apis/definitions/StorageApi.ts:31](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/StorageApi.ts#L31).
Referenced by: [forBucket](#forbucket).
@@ -158,7 +158,7 @@ export type StorageValueChange&lt;T = any&gt; = {
</pre>
Defined at
[packages/core-api/src/apis/definitions/StorageApi.ts:21](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/apis/definitions/StorageApi.ts#L21).
[packages/core-api/src/apis/definitions/StorageApi.ts:21](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/apis/definitions/StorageApi.ts#L21).
Referenced by: [observe\$](#observe), [StorageApi](#storageapi).
@@ -181,6 +181,6 @@ export type Subscription = {
</pre>
Defined at
[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/f8780ff32509d0326bc513791ea60846d7614b34/packages/core-api/src/types.ts#L33).
[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/82d329555c16af46db9b4e5cd2f44a3cc006a52e/packages/core-api/src/types.ts#L33).
Referenced by: [Observable](#observable).
+7 -7
View File
@@ -18,10 +18,10 @@ spec:
component: frontend
spec:
containers:
- name: app
image: spotify/backstage:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: app
protocol: TCP
- name: app
image: spotify/backstage:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: app
protocol: TCP
+7 -7
View File
@@ -18,10 +18,10 @@ spec:
component: backend
spec:
containers:
- name: backend
image: spotify/backstage-backend:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 7000
name: backend
protocol: TCP
- name: backend
image: spotify/backstage-backend:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 7000
name: backend
protocol: TCP
+1 -1
View File
@@ -1,5 +1,5 @@
apiVersion: v1
appVersion: "1.0"
appVersion: '1.0'
description: A Helm chart for Spotify Backstage
name: backstage
version: 0.1.1-alpha.12
+25 -19
View File
@@ -1,12 +1,12 @@
app:
enabled: true
nameOverride: ""
fullnameOverride: ""
nameOverride: ''
fullnameOverride: ''
replicaCount: 1
serviceAccount:
create: false
Name: ""
image:
Name: ''
image:
repository: spotify/backstage
tag: latest
pullPolicy: Always
@@ -15,20 +15,23 @@ app:
port: 80
ingress:
enabled: false
annotations: {}
annotations:
{}
# kubernetes.io/ingress.class: "nginx"
hosts:
- host: backstage.local
paths:
- /
- host: backstage.local
paths:
- /
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
imagePullSecrets: []
podSecurityContext: {}
podSecurityContext:
{}
# fsGroup: 2000
securityContext: {}
securityContext:
{}
# capabilities:
# drop:
# - ALL
@@ -48,12 +51,12 @@ app:
backend:
enabled: false
nameOverride: ""
fullnameOverride: ""
nameOverride: ''
fullnameOverride: ''
replicaCount: 1
serviceAccount:
create: false
Name: ""
Name: ''
image:
repository: spotify/backstage-backend
tag: latest
@@ -63,20 +66,23 @@ backend:
port: 7000
ingress:
enabled: false
annotations: {}
annotations:
{}
# kubernetes.io/ingress.class: "nginx"
hosts:
- host: backstage.local
paths:
- /backend
- host: backstage.local
paths:
- /backend
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
imagePullSecrets: []
podSecurityContext: {}
podSecurityContext:
{}
# fsGroup: 2000
securityContext: {}
securityContext:
{}
# capabilities:
# drop:
# - ALL
+11 -11
View File
@@ -7,14 +7,14 @@ metadata:
component: ingress
spec:
rules:
- host: <HOSTNAME>
http:
paths:
- backend:
serviceName: backstage
servicePort: frontend
path: /
- backend:
serviceName: backstage-backend
servicePort: backend
path: /backend
- host: <HOSTNAME>
http:
paths:
- backend:
serviceName: backstage
servicePort: frontend
path: /
- backend:
serviceName: backstage-backend
servicePort: backend
path: /backend
+8 -8
View File
@@ -11,10 +11,10 @@ spec:
app: backstage
component: frontend
ports:
- name: frontend
port: 80
protocol: TCP
targetPort: app
- name: frontend
port: 80
protocol: TCP
targetPort: app
---
apiVersion: v1
kind: Service
@@ -29,7 +29,7 @@ spec:
app: backstage
component: backend
ports:
- name: backend
port: 7000
protocol: TCP
targetPort: backend
- name: backend
port: 7000
protocol: TCP
targetPort: backend
+4
View File
@@ -0,0 +1,4 @@
# Build output
build
i18n
+25 -1
View File
@@ -2,12 +2,36 @@ This website was created with [Docusaurus](https://docusaurus.io/).
# What's In This Document
- [Get Started in 5 Minutes](#get-started-in-5-minutes)
- [Getting Started](#getting-started)
- [Directory Structure](#directory-structure)
- [Editing Content](#editing-content)
- [Adding Content](#adding-content)
- [Full Documentation](#full-documentation)
# Getting Started
## Installation
```
$ yarn install
```
## Local Development
```
$ yarn start
```
This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server.
## Build
```
$ yarn build
```
This command generates static content into the `build` directory, which is what will be deployed to GitHub pages from the master branch.
## Directory Structure
Your project file structure should look something like this
@@ -34,7 +34,7 @@ You get to take full advantage of a platform that we at Spotify have been using
Just run the backstage-cli:
```bash
npx @backstage/cli create-app
npx @backstage/create-app
```
Name your app, and we will create everything you need:
@@ -50,7 +50,7 @@ yarn start
And you are good to go! 👍
Read the full documentation on how to [create an app](/docs/getting-started/create-an-app.md) on GitHub.
Read the full documentation on how to [create an app](/docs/getting-started/create-an-app) on GitHub.
## What do I get? (Let's get technical...)
@@ -0,0 +1,122 @@
---
title: Announcing TechDocs: Spotifys docs-like-code plugin for Backstage
author: Gary Niemen
authorURL: https://github.com/garyniemen
---
Since we [open sourced Backstage](https://backstage.io/blog/2020/03/16/announcing-backstage), one of the most requested features has been for a technical documentation plugin. Well, good news. The first open source version of TechDocs is here. Now lets start collaborating and making it better, together.
<iframe width="780" height="440" src="https://www.youtube.com/embed/mOLCgdPw1iA" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen>
</iframe>
<!--truncate-->
Internally, we call it TechDocs. Its the most used plugin at Spotify by far — accounting for about 20% of our Backstage traffic (even though it is just one of 130+ plugins). Its popularity is evidence of something simple: We made documentation so easy to create, find, and use — people actually use it.
We are quite sure the main reason for the success of TechDocs is our docs-like-code approach — engineers write their technical documentation in Markdown files that live together with the code. During CI, a documentation site is created using MkDocs, and all sites are rendered centrally in a Backstage plugin. On top of the static documentation, we incorporate additional metadata about the documentation site — such as owner, open GitHub Issues, Slack support channel, and Stack Overflow Enterprise tags.
![available-templates](assets/announcing-techdocs/docs-in-backstage.png)
Watch this video to see [Spotifys internal version of TechDocs](https://backstage.io/demos) in action.
But this is just one way to do it. Today were most excited for what the open version of TechDocs can become.
## Okay, lets start collaborating
If you go to [GitHub](https://github.com/spotify/backstage/tree/master/plugins) now, youll find everything you need to start collaborating with us to build out the docs-like-code Backstage plugin — well call it TechDocs in the open as well.
Youll find the code in [techdocs](https://github.com/spotify/backstage/tree/master/plugins/techdocs) (frontend) and [techdocs-backend](https://github.com/spotify/backstage/tree/master/plugins/techdocs-backend). (There are also two separate packages [techdocs-cli](https://github.com/spotify/backstage/tree/master/packages/techdocs-cli) and [techdocs-container](https://github.com/spotify/backstage/tree/master/packages/techdocs-container).)
Youll find issues to work on in the [issues queue](https://github.com/spotify/backstage/issues?q=is%3Aissue+is%3Aopen+label%3A%22docs-like-code%22+label%3A%22help+wanted%22), typically starting with TechDocs: and labeled with docs-like-code, some labeled good first issue. Feel free to add your own issues, of course.
![available-templates](assets/announcing-techdocs/github-issues.png)
What we have on GitHub so far is a first iteration of TechDocs that you can use end-to-end — in other words, from docs written in Markdown in GitHub to a published site on Backstage.
More specifically, with this first iteration, you can:
- Run TechDocs locally and read documentation.
- Configure your entity (e.g. service, website) so that Backstage builds your documentation and serves it in TechDocs. Documentation is displayed on the Docs tab in the Service Catalog and on its own page.
- Get documentation set up for free in your project when you create a new component out of one of the non-experimental templates (labeled with recommended). If you are looking for a standalone documentation project, use the docs-template.
- Choose your own storage solution for the documentation.
- Define your own API to interface with your documentation solution.
For a full overview, including getting started instructions, check out our [TechDocs Documentation](https://backstage.io/docs/features/techdocs/techdocs-overview).
But before you go there, let me tell you a bit about the TechDocs story — and why we believe TechDocs is such a powerful yet simple solution for great documentation.
## The TechDocs story
Here is the TechDocs story. Its not an uncommon one (we have learned from many other companies).
About a year and a half ago, we conducted a company-wide productivity survey. The third largest problem according to all our engineers? Not being able to find the technical information they needed to do their work. And its not surprising. There was no standard way to produce and consume technical documentation, so teams were going their own way — using Confluence, Google Docs, README files, custom built websites, GitHub Pages, and so on and on. And those searching for information were left to hunt for it in all those different places until they found what they were looking for (if they ever did). Worse, if you did happen to find the documentation that you needed, there was no way to know whether the information was up-to-date or correct. In other words, there was no way to know whether you could trust what you found. We did have technical writers at the company, but they were mostly scattered across the company solving documentation problems within their own particular domain.
So this is the fertile soil on which TechDocs was built.
After a Hack Week implementation attracted interest from high up in the company, we formed a cross-functional team made up of technical writers and engineers with the mission to solve internal technical documentation at Spotify. And we started to build TechDocs. We went for a docs-like-code approach, fiercely optimizing for engineers and engineering workflows. We also went for an opinionated approach, telling everybody: This is the standard way to do technical documentation at Spotify. The sense was that engineers appreciated a documentation solution that was in line with their workflow and, after all the frustration of multiple tools, were relieved to be told “this is the way to do it”.
For more information about this journey, take a look at my 20-minute talk from DevRelCon London from last December: [The Heros Journey: How we are solving internal technical documentation at Spotify](https://www.linkedin.com/posts/garyniemen_how-we-are-solving-internal-technical-documentation-activity-6646078605594030080-4L31).
## Key problem areas that we are solving
We have come a long way, fast — both in our implementation and in our thinking. Here are some of the key problem areas that we are addressing. Note that they are in various stages of implementation, and we wont be able to release everything within our minimum plugin. In fact, see this as an appetite taster. What we hope is that we can build together.
### Stuck to unstuck
Very early on, we decided that the main problem we were trying to solve was to help engineers (when using technical documentation) go from stuck to unstuck, and fast. This became our guiding principle. Is what we are building helping engineers get unstuck faster? From this, it follows that we need to promote quality documentation on the one hand, and provide a high level of discoverability on the other. One without the other is not going to cut it.
### Feedback loops
What we want to build is a thriving community of technical documentation creators, contributors, and readers. We want this because, we believe, this is the way to drive up the quality of the documentation. More readers, more feedback, more doc updates. And driving up the quality of the corpus of technical documentation leads to trust which in turn leads to more engagement and, hence, more of a thriving community.
To get this working, we recognised that we need to remove friction from the system — we need to build in efficient feedback loops. For example, help engineers get their doc site up by providing documentation improvement hints and build information as close as possible to where they are already working. And for readers, make it easy to give feedback. And then for doc site owners, ensure that they are notified when there is feedback and incentivised to make the fix.
![available-templates](assets/announcing-techdocs/feedback-loop1.png)
![available-templates](assets/announcing-techdocs/feedback-loop2.png)
### Trust
How do I know whether to trust this piece of documentation? This is a question we want to be able to answer for those using technical documentation in Backstage. Its not an easy nut to crack. It is almost, one could say, the hard problem of technical documentation. For example, some might say last updated is a key factor. But what about stable, good quality documentation that has no need to be updated? What about page views? Yes, this is a good sign that the documentation is being found and viewed, but it doesnt say anything about whether the documentation can be trusted. How about a button: Did this documentation help? This is good, but will people use it? Will we get enough data to show trust? We have lofty ambitions of one day providing a trust score on the doc site informed by a super-intelligent algorithm. But we are not there yet. For now, we have landed on surfacing when the documentation was last updated, top five contributors, the support channel, owning team, and number of open GitHub Issues. But going forward we are definitely up for solving the hard problem. We think theres much more work to be done here and look forward to seeing ideas from the community.
### Discoverability and search
How to find stuff? That is another big question. As mentioned above, its all well and good having quality documentation, but its no use whatsoever if you cant find it. If you know what you are looking for, then you can use a search engine. If you dont know what you are looking for, then you are going to need more — like a well designed information architecture, a user friendly browse experience, and even intelligent suggestions based on your role and what you have searched for previously.
In this problem area, we made use of Elasticsearch, the open source search engine that was already being used in Backstage, to implement documentation search across all documentation sites and per documentation site. In terms of discoverability, we implemented a documentation home page in Backstage that surfaces Spotifys most important documents and uses metrics to list the companys most used doc sites as well as the documentation equivalent of a “your daily mix” playlist.
![available-templates](assets/announcing-techdocs/discover1.png)
![available-templates](assets/announcing-techdocs/discover2.png)
There is much more to do in the area of discoverability and search.
### Use case variations
The standard use case for TechDocs is: One component in Backstage equals one GitHub repository, equals one doc site. This use case comes in two flavours: the repository is a code repository with docs or a docs-only repository. Then, to meet the needs of one large part of the Spotify engineering organisation that uses monorepos (multi-component repositories), we added a third use case. We built an MkDocs plugin that enabled doc site creators to include documentation from doc folders in other parts of the repository. So this use case is: One main component in Backstage equals a monorepo with distributed documentation, equals one doc site.
These three use cases satisfy most of the needs, but we have had plenty of requests for additional use cases, for example, the ability to create multiple doc sites from a multi-component repository and the ability to create one doc site from documentation in multiple repositories.
### Metrics
There are many good arguments for standardizing the way that technical documentation is produced and consumed. One of them is metrics. If we have one way of producing technical documentation (in our case, GitHub Enterprise) and one place where it shows up (in our case, Backstage), we are in a strong position to build up metrics that help all the various stakeholders — for example, us building TechDocs, teams creating documentation sites, and engineers trying to get unstuck. Just imagine how much harder this would be if technical documentation was produced and consumed in a plethora of places, such as Confluence, Google Docs, README files, custom web sites, and GitHub Pages.
One thing we have recently completed is a Manage page in Backstage for doc site owners. Here teams can see all the documentation that they own, the number of GitHub Issues per doc site or page, and last updated. We have also built a large dashboard using the open source analytics software Redash to inform our own product development process.
![available-templates](assets/announcing-techdocs/metrics.png)
Again, there is a lot more that can be done in the area of metrics. Did I mention the trust score?
### Code-like-docs
Code-like-docs, what? Okay, its just my little play on words. This is what I mean. One request that we keep getting is to be able to have code in the documentation fetched from and in sync with code in GitHub. In this way, you can avoid code in the documentation going stale. MkDocs does have an extension for this — but it has some limitations. For example, the code has to be in the /docs folder with the Markdown files. We are working on developing a wider and more flexible solution.
### Golden Paths
At Spotify, we have the concept of [Golden Paths](https://engineering.atspotify.com/2020/08/17/how-we-use-golden-paths-to-solve-fragmentation-in-our-software-ecosystem/) — one for each engineering discipline. My favourite definition of Golden Path is that it is the “opinionated and supported path”. Each Golden Path has an accompanying Golden Path tutorial that walks you through the opinionated and supported path.
The Golden Path tutorials are Spotifys most used and important documents and have shown themselves to be the most challenging to manage within a docs-like-code environment. One reason for this is that they are long, divided into many parts, and ownership is typically spread among many different teams. We have had to make use of GitHub codeowners to handle ownership and had to create datasets and data pipelines to be able to attach GitHub Issues to the specific parts or files that a team owns. Another challenge of the Golden Path tutorials is that parts are often dependent on other parts. We are just starting to look into how we can solve these dependency challenges in order to remove friction for engineers writing tutorial documentation.
---
So thats it for now. As you can see, we have come a long way AND there is much more to do. We are looking forward to continuing our docs-like-code journey out in the open with new, enthusiastic technical documentation friends.
Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

+3
View File
@@ -45,6 +45,9 @@ class Footer extends React.Component {
<div>
<h5>Community</h5>
<a href="https://discord.gg/MUpMjP2">Support chatroom</a>
<a href="https://github.com/spotify/backstage/blob/master/CONTRIBUTING.md">
Contributing
</a>
<a href="https://mailchi.mp/spotify/backstage-community">
Subscribe to our newsletter
</a>
+9
View File
@@ -0,0 +1,9 @@
---
title: API Docs
author: SDA SE
authorUrl: https://sda.se/
category: Discovery
description: Components to discover and display API entities as an extension to the catalog plugin.
documentation: https://github.com/spotify/backstage/blob/master/plugins/api-docs/README.md
iconUrl: https://thecoders.io/wp-content/uploads/2019/11/tech-swagger.svg
npmPackageName: '@backstage/plugin-api-docs'
+5 -2
View File
@@ -1,9 +1,12 @@
---
title: CircleCI
author: Spotify
authorUrl: https://www.spotify.com/
authorUrl: https://github.com/spotify
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
iconUrl: https://www.saaves.com/storage/brochure/logo-circleci-icon1583764538.png
npmPackageName: '@backstage/plugin-circleci'
tags:
- ci
- cd
@@ -0,0 +1,13 @@
---
title: GitHub Actions
author: Spotify
authorUrl: https://github.com/spotify
category: CI
description: GitHub Actions makes it easy to automate all your software workflows, now with world-class CI/CD. Build, test, and deploy your code right from GitHub.
documentation: https://github.com/spotify/backstage/tree/master/plugins/github-actions
iconUrl: https://avatars2.githubusercontent.com/u/44036562?s=400&v=4
npmPackageName: '@backstage/plugin-github-actions'
tags:
- ci
- cd
- github
@@ -0,0 +1,9 @@
---
title: GitHub Pull Requests
author: roadie.io
authorUrl: https://roadie.io/
category: CI
description: View GitHub pull requests for your service in Backstage.
documentation: https://roadie.io/backstage/plugins/github-pull-requests
iconUrl: https://roadie.io/static/7f13bb8d861d8dedc5112fb939d215f9/351f2/GitHub-Mark-Light-120px-plus.png
npmPackageName: '@roadiehq/backstage-plugin-github-pull-requests'
+4 -5
View File
@@ -1,13 +1,12 @@
---
title: GraphiQL
author: Spotify
authorUrl: https://www.spotify.com/
authorUrl: https://github.com/spotify
category: Debugging
description: Integrates GraphiQL as a tool to browse GraphiQL endpoints inside Backstage.
documentation: https://github.com/spotify/backstage/tree/master/plugins/lighthouse
description: Integrates GraphiQL as a tool to browse GraphQL API endpoints inside Backstage.
documentation: https://github.com/spotify/backstage/tree/master/plugins/graphiql
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
- graphiql
+12
View File
@@ -0,0 +1,12 @@
---
title: Jenkins
author: '@timja'
authorUrl: https://github.com/timja
category: CI
description: Jenkins offers a simple way to set up a continuous integration and continuous delivery environment.
documentation: https://github.com/spotify/backstage/tree/master/plugins/jenkins
iconUrl: https://img.icons8.com/color/1600/jenkins.png
npmPackageName: '@backstage/plugin-jenkins'
tags:
- ci
- cd
+1 -1
View File
@@ -1,7 +1,7 @@
---
title: Lighthouse
author: Spotify
authorUrl: https://www.spotify.com/
authorUrl: https://github.com/spotify
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
-1
View File
@@ -7,4 +7,3 @@ 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'
+1 -2
View File
@@ -1,10 +1,9 @@
---
title: Sentry
author: Spotify
authorUrl: https://www.spotify.com/
authorUrl: https://github.com/spotify
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'
+2 -2
View File
@@ -1,9 +1,9 @@
---
title: Tech Radar
author: Spotify
authorUrl: https://www.spotify.com/
authorUrl: https://github.com/spotify
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
iconUrl: https://www.materialui.co/materialIcons/action/track_changes_white_192x192.png
npmPackageName: '@backstage/plugin-tech-radar'
-1
View File
@@ -7,4 +7,3 @@ 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'
-325
View File
@@ -1,325 +0,0 @@
{
"_comment": "This file is auto-generated by write-translations.js",
"localized-strings": {
"next": "Next",
"previous": "Previous",
"tagline": "An open platform for building developer portals",
"docs": {
"api/backend": {
"title": "Backend"
},
"api/utility-apis": {
"title": "Utility APIs"
},
"architecture-decisions/adrs-adr001": {
"title": "ADR001: Architecture Decision Record (ADR) log",
"sidebar_label": "ADR001"
},
"architecture-decisions/adrs-adr002": {
"title": "ADR002: Default Software Catalog File Format",
"sidebar_label": "ADR002"
},
"architecture-decisions/adrs-adr003": {
"title": "ADR003: Avoid Default Exports and Prefer Named Exports",
"sidebar_label": "ADR003"
},
"architecture-decisions/adrs-adr004": {
"title": "ADR004: Module Export Structure",
"sidebar_label": "ADR004"
},
"architecture-decisions/adrs-adr005": {
"title": "ADR005: Catalog Core Entities",
"sidebar_label": "ADR005"
},
"architecture-decisions/adrs-adr006": {
"title": "ADR006: Avoid React.FC and React.SFC",
"sidebar_label": "ADR006"
},
"architecture-decisions/adrs-adr007": {
"title": "ADR007: Use MSW to mock http requests",
"sidebar_label": "ADR007"
},
"architecture-decisions/adrs-adr008": {
"title": "ADR008: Default Catalog File Name",
"sidebar_label": "ADR008"
},
"architecture-decisions/adrs-overview": {
"title": "Architecture Decision Records (ADR)",
"sidebar_label": "Overview"
},
"auth/add-auth-provider": {
"title": "Adding authentication providers"
},
"auth/auth-backend-classes": {
"title": "Auth backend classes"
},
"auth/auth-backend": {
"title": "Auth backend"
},
"auth/glossary": {
"title": "Glossary"
},
"auth/index": {
"title": "User Authentication and Authorization in Backstage"
},
"auth/oauth": {
"title": "OAuth and OpenID Connect"
},
"conf/defining": {
"title": "Defining Configuration for your Plugin"
},
"conf/index": {
"title": "Static Configuration in Backstage"
},
"conf/reading": {
"title": "Reading Backstage Configuration"
},
"conf/writing": {
"title": "Writing Backstage Configuration Files"
},
"dls/contributing-to-storybook": {
"title": "Contributing to Storybook"
},
"dls/design": {
"title": "Design"
},
"dls/figma": {
"title": "Figma"
},
"FAQ": {
"title": "FAQ"
},
"features/software-catalog/software-catalog-api": {
"title": "API"
},
"features/software-catalog/descriptor-format": {
"title": "Descriptor Format of Catalog Entities",
"sidebar_label": "YAML File Format"
},
"features/software-catalog/extending-the-model": {
"title": "Extending the model"
},
"features/software-catalog/external-integrations": {
"title": "External integrations"
},
"features/software-catalog/software-catalog-overview": {
"title": "Backstage Service Catalog (alpha)",
"sidebar_label": "Backstage Service Catalog"
},
"features/software-catalog/installation": {
"title": "features/software-catalog/installation"
},
"features/software-catalog/system-model": {
"title": "System Model"
},
"features/software-templates/adding-templates": {
"title": "Adding your own Templates"
},
"features/software-templates/extending/extending-preparer": {
"title": "Create your own Preparer"
},
"features/software-templates/extending/extending-publisher": {
"title": "Create your own Publisher"
},
"features/software-templates/extending/extending-templater": {
"title": "Creating your own Templater"
},
"features/software-templates/extending/extending-index": {
"title": "Extending the Scaffolder"
},
"features/software-templates/software-templates-index": {
"title": "Software Templates"
},
"features/software-templates/installation": {
"title": "features/software-templates/installation"
},
"features/techdocs/concepts": {
"title": "Concepts"
},
"features/techdocs/creating-and-publishing": {
"title": "Creating and publishing your docs",
"sidebar_label": "Creating and Publishing Documentation"
},
"features/techdocs/faqs": {
"title": "TechDocs FAQ",
"sidebar_label": "FAQ"
},
"features/techdocs/getting-started": {
"title": "Getting Started"
},
"features/techdocs/techdocs-overview": {
"title": "TechDocs Documentation",
"sidebar_label": "Overview"
},
"getting-started/app-custom-theme": {
"title": "Customize the look-and-feel of your App"
},
"getting-started/configure-app-with-plugins": {
"title": "Configuring App with plugins"
},
"getting-started/create-an-app": {
"title": "Create an App"
},
"getting-started/deployment-k8s": {
"title": "Kubernetes"
},
"getting-started/deployment-other": {
"title": "Other"
},
"getting-started/development-environment": {
"title": "Development Environment"
},
"getting-started/index": {
"title": "Running Backstage Locally"
},
"getting-started/installation": {
"title": "Installation"
},
"overview/adopting": {
"title": "Strategies for adopting"
},
"overview/architecture-overview": {
"title": "Architecture overview"
},
"overview/architecture-terminology": {
"title": "Architecture terminology"
},
"overview/background": {
"title": "The Spotify Story"
},
"overview/roadmap": {
"title": "Project roadmap"
},
"overview/support": {
"title": "Support and community"
},
"overview/vision": {
"title": "Vision"
},
"overview/what-is-backstage": {
"title": "What is Backstage?"
},
"plugins/add-to-marketplace": {
"title": "Add to Marketplace"
},
"plugins/backend-plugin": {
"title": "Backend plugin"
},
"plugins/call-existing-api": {
"title": "Call Existing API"
},
"plugins/create-a-plugin": {
"title": "Create a Backstage Plugin"
},
"plugins/existing-plugins": {
"title": "Existing plugins"
},
"plugins/index": {
"title": "Intro to plugins"
},
"plugins/plugin-development": {
"title": "Plugin Development"
},
"plugins/proxying": {
"title": "Proxying"
},
"plugins/publish-private": {
"title": "Publish private"
},
"plugins/publishing": {
"title": "Publishing"
},
"plugins/structure-of-a-plugin": {
"title": "Structure of a Plugin"
},
"plugins/testing": {
"title": "Testing with Jest"
},
"README": {
"title": "README"
},
"reference/createPlugin-feature-flags": {
"title": "createPlugin - feature flags"
},
"reference/createPlugin-router": {
"title": "createPlugin - router"
},
"reference/createPlugin": {
"title": "createPlugin"
},
"reference/utility-apis/AlertApi": {
"title": "reference/utility-apis/AlertApi"
},
"reference/utility-apis/AppThemeApi": {
"title": "reference/utility-apis/AppThemeApi"
},
"reference/utility-apis/BackstageIdentityApi": {
"title": "reference/utility-apis/BackstageIdentityApi"
},
"reference/utility-apis/Config": {
"title": "reference/utility-apis/Config"
},
"reference/utility-apis/ErrorApi": {
"title": "reference/utility-apis/ErrorApi"
},
"reference/utility-apis/FeatureFlagsApi": {
"title": "reference/utility-apis/FeatureFlagsApi"
},
"reference/utility-apis/IdentityApi": {
"title": "reference/utility-apis/IdentityApi"
},
"reference/utility-apis/OAuthApi": {
"title": "reference/utility-apis/OAuthApi"
},
"reference/utility-apis/OAuthRequestApi": {
"title": "reference/utility-apis/OAuthRequestApi"
},
"reference/utility-apis/OpenIdConnectApi": {
"title": "reference/utility-apis/OpenIdConnectApi"
},
"reference/utility-apis/ProfileInfoApi": {
"title": "reference/utility-apis/ProfileInfoApi"
},
"reference/utility-apis/README": {
"title": "Utility API References"
},
"reference/utility-apis/SessionStateApi": {
"title": "reference/utility-apis/SessionStateApi"
},
"reference/utility-apis/StorageApi": {
"title": "reference/utility-apis/StorageApi"
},
"tutorials/journey": {
"title": "Future developer journey"
}
},
"links": {
"GitHub": "GitHub",
"Docs": "Docs",
"Plugins": "Plugins",
"Blog": "Blog",
"Demos": "Demos",
"Newsletter": "Newsletter"
},
"categories": {
"Overview": "Overview",
"Getting Started": "Getting Started",
"Features": "Features",
"Plugins": "Plugins",
"Configuration": "Configuration",
"Auth and identity": "Auth and identity",
"Designing for Backstage": "Designing for Backstage",
"API references": "API references",
"Tutorials": "Tutorials",
"Architecture Decision Records (ADRs)": "Architecture Decision Records (ADRs)",
"Contribute": "Contribute",
"Support": "Support",
"FAQ": "FAQ"
}
},
"pages-strings": {
"Help Translate|recruit community translators for your project": "Help Translate",
"Edit this Doc|recruitment message asking to edit the doc source": "Edit",
"Translate this Doc|recruitment message asking to translate the docs": "Translate"
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
{
"version": "0.1.1-alpha.20",
"version": "0.0.0",
"name": "backstage-microsite",
"license": "Apache-2.0",
"private": true,
+35
View File
@@ -78,6 +78,41 @@ const Background = props => {
</Block.Container>
</Block>
<Block className="stripe-bottom bg-black-grey">
<Block.Container style={{ justifyContent: 'flex-start' }}>
<Block.TextBox>
<Block.Title id="techdocs-demo">
Make documentation easy
</Block.Title>
<Block.Paragraph>
Documentation! Everyone needs it, no one wants to create it, and
no one can ever find it. Backstage follows a docs like code
approach: you write documentation in Markdown files right
alongside your code. This makes documentation easier to create,
maintain, find and, you know, actually use. This demo video
showcases Spotifys internal version of TechDocs. Learn more about{' '}
<a href="https://backstage.io/blog/2020/09/08/announcing-tech-docs">
TechDocs
</a>
.
</Block.Paragraph>
<Block.LinkButton href={'https://youtu.be/mOLCgdPw1iA'}>
Watch now
</Block.LinkButton>
</Block.TextBox>
<Block.MediaFrame>
<iframe
width="560"
height="315"
src="https://www.youtube.com/embed/mOLCgdPw1iA"
frameborder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
></iframe>
</Block.MediaFrame>
</Block.Container>
</Block>
<Block small className="bg-black-grey">
<Block.Container style={{ justifyContent: 'flex-start' }}>
<Block.TextBox>
+7 -21
View File
@@ -296,9 +296,7 @@ class Index extends React.Component {
src={`${baseUrl}animations/backstage-techdocs-icon-1.gif`}
/>
<Block.Subtitle>
Backstage TechDocs <span>(Coming Soon)</span>
</Block.Subtitle>
<Block.Subtitle>Backstage TechDocs</Block.Subtitle>
<Block.Title small>Docs like code</Block.Title>
</Block.TextBox>
<Breakpoint
@@ -373,19 +371,11 @@ class Index extends React.Component {
</Block>
<ActionBlock className="stripe bg-teal">
<ActionBlock.Title>Subscribe to our newsletter</ActionBlock.Title>
<ActionBlock.Subtitle>
TechDocs is our most used feature at Spotify. Be the first to know
when{' '}
<a href="https://github.com/spotify/backstage/projects/5">
the open source version
</a>{' '}
ships.
</ActionBlock.Subtitle>
<ActionBlock.Title>Learn more about TechDocs</ActionBlock.Title>
<ActionBlock.Link
href={`https://mailchi.mp/spotify/backstage-community`}
href={`https://backstage.io/docs/features/techdocs/techdocs-overview`}
>
Subscribe
Docs
</ActionBlock.Link>
</ActionBlock>
@@ -455,12 +445,8 @@ class Index extends React.Component {
Share with the community
</Block.SmallTitle>
<Block.Paragraph>
Building{' '}
<a href="https://github.com/spotify/backstage/blob/master/docs/FAQ.md#how-do-i-find-out-if-a-plugin-already-exists">
open source plugins
</a>{' '}
contributes to the entire Backstage ecosystem, which benefits
everyone
Building <a href="/plugins">open source plugins</a> contributes
to the entire Backstage ecosystem, which benefits everyone
</Block.Paragraph>
</Block.TextBox>
@@ -472,7 +458,7 @@ class Index extends React.Component {
<ActionBlock className="stripe-top bg-teal">
<ActionBlock.Title>Build a plugin</ActionBlock.Title>
<ActionBlock.Link href="https://github.com/spotify/backstage/blob/master/docs/plugins/create-a-plugin.md">
<ActionBlock.Link href="/docs/plugins/create-a-plugin">
Contribute
</ActionBlock.Link>
</ActionBlock>
+38 -3
View File
@@ -29,13 +29,17 @@ const Plugins = () => (
<main className="MainContent">
<div className="PluginPageLayout">
<div className="PluginPageHeader">
<h2>Plugins</h2>
<h2>Plugin marketplace</h2>
<p>
Open source plugins that you can add to your Backstage deployment.
Learn how to build a <a href="/docs/plugins">plugin</a>.
</p>
<span>
<a
className="PluginAddNewButton ButtonFilled"
href={addPluginDocsLink}
>
<b>Add Plugin</b>
<b>Add to marketplace</b>
</a>
</span>
</div>
@@ -69,13 +73,44 @@ const Plugins = () => (
className="PluginCardLink ButtonFilled"
href={documentation}
>
docs
Explore
</a>
</span>
</Container>
</div>
),
)}
<div className="PluginCard" id="add-plugin-card">
<div className="PluginCardBody">
<p>
Do you have an existing plugin that you want to add to the
Marketplace?
</p>
<p
style={{
marginTop: '20px',
textAlign: 'center',
}}
>
<a className="ButtonFilled" href={addPluginDocsLink}>
<b>Add to marketplace</b>
</a>
</p>
</div>
<Container className="PluginCardFooter">
<p>
See what plugins are already{' '}
<a href="https://github.com/spotify/backstage/issues?q=is%3Aissue+is%3Aopen+label%3Aplugin+sort%3Areactions-%2B1-desc">
in progress
</a>{' '}
and 👍. Missing a plugin for your favorite tool? Please{' '}
<a href="https://github.com/spotify/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME">
suggest
</a>{' '}
a new one.
</p>
</Container>
</div>
</Container>
</div>
</main>
+8 -3
View File
@@ -31,7 +31,7 @@
]
}
],
"Features": [
"Core Features": [
{
"type": "subcategory",
"label": "Software Catalog",
@@ -46,7 +46,7 @@
},
{
"type": "subcategory",
"label": "Software creation templates",
"label": "Software Templates",
"ids": [
"features/software-templates/software-templates-index",
"features/software-templates/adding-templates",
@@ -74,6 +74,7 @@
"plugins/create-a-plugin",
"plugins/plugin-development",
"plugins/structure-of-a-plugin",
"plugins/integrating-plugin-into-service-catalog",
{
"type": "subcategory",
"label": "Backends and APIs",
@@ -91,7 +92,11 @@
{
"type": "subcategory",
"label": "Publishing",
"ids": ["plugins/publishing", "plugins/publish-private", "plugins/add-to-marketplace"]
"ids": [
"plugins/publishing",
"plugins/publish-private",
"plugins/add-to-marketplace"
]
}
],
"Configuration": [

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