feat(docs): autogenerate documentation from the API specs
Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com>
This commit is contained in:
@@ -266,4 +266,6 @@ module.exports = {
|
||||
settings: {
|
||||
'testing-library/custom-queries': 'off',
|
||||
},
|
||||
// Autogenerated code.
|
||||
ignorePatterns: ['docs/**/sidebar.ts']
|
||||
};
|
||||
|
||||
@@ -255,6 +255,7 @@ misconfiguration
|
||||
misconfigured
|
||||
mkdocs
|
||||
Mkdocs
|
||||
modularization
|
||||
monorepo
|
||||
Monorepo
|
||||
monorepos
|
||||
|
||||
@@ -80,6 +80,22 @@ jobs:
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
- name: microsite yarn install
|
||||
run: yarn install --immutable
|
||||
working-directory: microsite
|
||||
|
||||
- name: build OpenAPI API docs
|
||||
working-directory: microsite
|
||||
run: yarn docusaurus gen-api-docs all
|
||||
|
||||
- name: upload OpenAPI API docs
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4
|
||||
with:
|
||||
name: stable-openapi-docs
|
||||
path: docs/**/*.api.mdx
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
next:
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
@@ -137,6 +153,22 @@ jobs:
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
- name: microsite yarn install
|
||||
run: yarn install --immutable
|
||||
working-directory: microsite
|
||||
|
||||
- name: build OpenAPI API docs
|
||||
working-directory: microsite
|
||||
run: yarn docusaurus gen-api-docs all
|
||||
|
||||
- name: upload OpenAPI API docs
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4
|
||||
with:
|
||||
name: next-openapi-docs
|
||||
path: docs/**/*.api.mdx
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
deploy-microsite-and-storybook:
|
||||
permissions:
|
||||
contents: write # for JamesIves/github-pages-deploy-action to push changes in repo
|
||||
@@ -184,6 +216,12 @@ jobs:
|
||||
name: stable-reference
|
||||
path: docs/reference
|
||||
|
||||
- name: download stable OpenAPI API docs
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
|
||||
with:
|
||||
name: stable-openapi-docs
|
||||
path: docs/**/*.api.mdx
|
||||
|
||||
- name: grab lastest releases docs
|
||||
run: |
|
||||
git fetch origin master --depth 1
|
||||
@@ -196,6 +234,9 @@ jobs:
|
||||
- name: clear API reference
|
||||
run: rm -r docs/reference
|
||||
|
||||
- name: clear OpenAPI reference
|
||||
run: find . -name '*.api.mdx' -delete
|
||||
|
||||
# Next docs
|
||||
- name: checkout master
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
@@ -212,6 +253,12 @@ jobs:
|
||||
name: next-reference
|
||||
path: docs/reference
|
||||
|
||||
- name: download next OpenAPI API docs
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
|
||||
with:
|
||||
name: next-openapi-docs
|
||||
path: docs/**/*.api.mdx
|
||||
|
||||
- name: build microsite
|
||||
run: yarn build
|
||||
working-directory: microsite
|
||||
|
||||
@@ -45,6 +45,11 @@ bower_components
|
||||
# Documentation reference, generated by build:api-docs
|
||||
docs/reference
|
||||
|
||||
# OpenAPI auto-generated API documentation.
|
||||
docs/**/*.api.mdx
|
||||
docs/**/*.info.mdx
|
||||
docs/**/sidebar.ts
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
id: 404
|
||||
title: Unable to find API docs
|
||||
sidebar_label: API
|
||||
description: Unable to find the requested API docs.
|
||||
---
|
||||
|
||||
## API Docs Missing
|
||||
|
||||
If you're seeing this page at backstage.io/docs, please open an issue in the main repository. Something is wrong.
|
||||
|
||||
If you're seeing this page locally and you want to see the actual API docs for this plugin, you need to
|
||||
|
||||
1. cd into the `microsite/` directory.
|
||||
2. run `yarn generate-openapi-docs` to generate the API docs.
|
||||
3. rerun `yarn start` to pick up the new generated docs.
|
||||
@@ -23,6 +23,7 @@ import type * as Preset from '@docusaurus/preset-classic';
|
||||
import { Config } from '@docusaurus/types';
|
||||
import RedirectPlugin from '@docusaurus/plugin-client-redirects';
|
||||
import { releases } from './releases';
|
||||
import type * as OpenApiPlugin from "docusaurus-plugin-openapi-docs";
|
||||
|
||||
const backstageTheme = themes.vsDark;
|
||||
backstageTheme.plain.backgroundColor = '#232323';
|
||||
@@ -54,6 +55,14 @@ const PatchedRedirectPlugin: typeof RedirectPlugin = (ctx, opts) => {
|
||||
};
|
||||
};
|
||||
|
||||
const defaultOpenApiOptions = {
|
||||
hideSendButton: true,
|
||||
sidebarOptions: {
|
||||
groupPathsBy: 'tag',
|
||||
categoryLinkSource: "tag",
|
||||
},
|
||||
} satisfies OpenApiPlugin.Options
|
||||
|
||||
const config: Config = {
|
||||
title: 'Backstage Software Catalog and Developer Platform',
|
||||
tagline: 'An open source framework for building developer portals',
|
||||
@@ -110,6 +119,7 @@ const config: Config = {
|
||||
},
|
||||
}
|
||||
: undefined),
|
||||
docItemComponent: "@theme/ApiItem",
|
||||
},
|
||||
blog: {
|
||||
path: 'blog',
|
||||
@@ -266,7 +276,27 @@ const config: Config = {
|
||||
ratingMode: 'stars',
|
||||
},
|
||||
],
|
||||
[
|
||||
'docusaurus-plugin-openapi-docs',
|
||||
{
|
||||
id: "api", // plugin id
|
||||
docsPluginId: "classic", // configured for preset-classic
|
||||
config: {
|
||||
catalog: {
|
||||
...defaultOpenApiOptions,
|
||||
specPath: "../plugins/catalog-backend/src/schema/openapi.yaml",
|
||||
outputDir: "../docs/features/software-catalog/api",
|
||||
} satisfies OpenApiPlugin.Options,
|
||||
search: {
|
||||
...defaultOpenApiOptions,
|
||||
specPath: "../plugins/search-backend/src/schema/openapi.yaml",
|
||||
outputDir: "../docs/features/search/api",
|
||||
} satisfies OpenApiPlugin.Options,
|
||||
}
|
||||
},
|
||||
]
|
||||
],
|
||||
themes: ["docusaurus-theme-openapi-docs"],
|
||||
themeConfig: {
|
||||
colorMode: {
|
||||
defaultMode: 'dark',
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"build": "node scripts/pre-build.js && docusaurus build",
|
||||
"deploy": "docusaurus deploy",
|
||||
"docusaurus": "docusaurus",
|
||||
"generate-openapi-docs": "yarn docusaurus clean-api-docs all && yarn docusaurus gen-api-docs all",
|
||||
"prettier:check": "prettier --check .",
|
||||
"prettier:fix": "prettier --write .",
|
||||
"publish-gh-pages": "docusaurus-publish",
|
||||
@@ -18,6 +19,9 @@
|
||||
"write-translations": "docusaurus-write-translations"
|
||||
},
|
||||
"prettier": "@backstage/cli/config/prettier",
|
||||
"resolutions": {
|
||||
"node-polyfill-webpack-plugin": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "^3.1.1",
|
||||
"@docusaurus/plugin-client-redirects": "^3.1.1",
|
||||
@@ -26,8 +30,10 @@
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"@swc/core": "^1.3.46",
|
||||
"clsx": "^2.0.0",
|
||||
"docusaurus-plugin-openapi-docs": "^4.3.0",
|
||||
"docusaurus-plugin-sass": "^0.2.3",
|
||||
"docusaurus-pushfeedback": "^1.0.0",
|
||||
"docusaurus-theme-openapi-docs": "^4.3.0",
|
||||
"luxon": "^3.0.0",
|
||||
"prism-react-renderer": "^2.1.0",
|
||||
"react": "^18.0.2",
|
||||
|
||||
+39
-3
@@ -1,6 +1,17 @@
|
||||
const { releases } = require('./releases');
|
||||
import { releases } from './releases';
|
||||
|
||||
module.exports = {
|
||||
function tryToLoadCustomSidebar(ref){
|
||||
try{
|
||||
return require(ref)
|
||||
} catch (e) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
const catalogSidebar = tryToLoadCustomSidebar("../docs/features/software-catalog/api/sidebar.ts")
|
||||
const searchSidebar = tryToLoadCustomSidebar("../docs/features/search/api/sidebar.ts")
|
||||
|
||||
export default {
|
||||
docs: {
|
||||
Overview: [
|
||||
'overview/what-is-backstage',
|
||||
@@ -136,6 +147,19 @@ module.exports = {
|
||||
'features/search/search-overview',
|
||||
'features/search/getting-started',
|
||||
'features/search/concepts',
|
||||
{
|
||||
type: "category",
|
||||
label: "API",
|
||||
link: searchSidebar.length > 0 ? {
|
||||
type: "generated-index",
|
||||
title: "Search API",
|
||||
slug: "/category/search-api",
|
||||
} : {
|
||||
type: "doc",
|
||||
id: "openapi/generated-docs/404",
|
||||
},
|
||||
items: searchSidebar,
|
||||
},
|
||||
'features/search/architecture',
|
||||
'features/search/search-engines',
|
||||
'features/search/collators',
|
||||
@@ -158,7 +182,19 @@ module.exports = {
|
||||
'features/software-catalog/extending-the-model',
|
||||
'features/software-catalog/external-integrations',
|
||||
'features/software-catalog/catalog-customization',
|
||||
'features/software-catalog/software-catalog-api',
|
||||
{
|
||||
type: "category",
|
||||
label: "API",
|
||||
link: catalogSidebar.length > 0 ? {
|
||||
type: "generated-index",
|
||||
title: "Catalog API",
|
||||
slug: "/category/catalog-api",
|
||||
}: {
|
||||
type: "doc",
|
||||
id: "openapi/generated-docs/404",
|
||||
},
|
||||
items: catalogSidebar,
|
||||
},
|
||||
'features/software-catalog/creating-the-catalog-graph',
|
||||
'features/software-catalog/faq',
|
||||
],
|
||||
|
||||
+3466
-44
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,24 @@ openapi: 3.0.3
|
||||
info:
|
||||
title: catalog
|
||||
version: '1'
|
||||
description: The Backstage backend plugin that provides the Backstage catalog
|
||||
description: |
|
||||
The API surface consists of a few distinct groups of functionality. Each has a
|
||||
dedicated section below.
|
||||
|
||||
> **Note:** This page only describes some of the most commonly used parts of the
|
||||
> API, and is a work in progress.
|
||||
|
||||
All of the URL paths in this article are assumed to be on top of some base URL
|
||||
pointing at your catalog installation. For example, if the path given in a
|
||||
section below is `/entities`, and the catalog is located at
|
||||
`http://localhost:7007/api/catalog` during local development, the full URL would
|
||||
be `http://localhost:7007/api/catalog/entities`. The actual URL may vary from
|
||||
one organization to the other, especially in production, but is commonly your
|
||||
`backend.baseUrl` in your app config, plus `/api/catalog` at the end.
|
||||
|
||||
Some or all of the endpoints may accept or require an `Authorization` header
|
||||
with a `Bearer` token, which should then be the Backstage token returned by the
|
||||
[`identity API`](https://backstage.io/docs/reference/core-plugin-api.identityapiref).
|
||||
license:
|
||||
name: Apache-2.0
|
||||
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
@@ -44,7 +61,29 @@ components:
|
||||
cursor:
|
||||
name: cursor
|
||||
in: query
|
||||
description: Cursor to a set page of results.
|
||||
description: |
|
||||
You may pass the `cursor` query parameters to perform cursor based pagination
|
||||
through the set of entities. The value of `cursor` will be returned in the response, under the `pageInfo` property:
|
||||
|
||||
```json
|
||||
"pageInfo": {
|
||||
"nextCursor": "a-cursor",
|
||||
"prevCursor": "another-cursor"
|
||||
}
|
||||
```
|
||||
|
||||
If `nextCursor` exists, it can be used to retrieve the next batch of entities. Following the same approach,
|
||||
if `prevCursor` exists, it can be used to retrieve the previous batch of entities.
|
||||
|
||||
- [`filter`](#filtering), for selecting only a subset of all entities
|
||||
- [`fields`](#field-selection), for selecting only parts of the full data
|
||||
structure of each entity
|
||||
- `limit` for limiting the number of entities returned (20 is the default)
|
||||
- [`orderField`](#ordering), for deciding the order of the entities
|
||||
- `fullTextFilter`
|
||||
**NOTE**: [`filter`, `orderField`, `fullTextFilter`] and `cursor` are mutually exclusive. This means that,
|
||||
it isn't possible to change any of [`filter`, `orderField`, `fullTextFilter`] when passing `cursor` as query parameters,
|
||||
as changing any of these properties will affect pagination. If any of `filter`, `orderField`, `fullTextFilter` is specified together with `cursor`, only the latter is taken into consideration.
|
||||
required: false
|
||||
allowReserved: true
|
||||
schema:
|
||||
@@ -62,7 +101,25 @@ components:
|
||||
fields:
|
||||
name: fields
|
||||
in: query
|
||||
description: Restrict to just these fields in the response.
|
||||
description: |
|
||||
By default the full entities are returned, but you can pass in a `fields` query
|
||||
parameter which selects what parts of the entity data to retain. This makes the
|
||||
response smaller and faster to transfer, and may allow the catalog to perform
|
||||
more efficient queries.
|
||||
|
||||
The query parameter value is a comma separated list of simplified JSON paths
|
||||
like above. Each path corresponds to the key of either a value, or of a subtree
|
||||
root that you want to keep in the output. The rest is pruned away. For example,
|
||||
specifying `?fields=metadata.name,metadata.annotations,spec` retains only the
|
||||
`name` and `annotations` fields of the `metadata` of each entity (it'll be an
|
||||
object with at most two keys), keeps the entire `spec` unchanged, and cuts out
|
||||
all other roots such as `relations`.
|
||||
|
||||
Some more real world usable examples:
|
||||
|
||||
- Return only enough data to form the full ref of each entity:
|
||||
|
||||
`/entities/by-query?fields=kind,metadata.namespace,metadata.name`
|
||||
required: false
|
||||
allowReserved: true
|
||||
explode: false
|
||||
@@ -83,7 +140,91 @@ components:
|
||||
filter:
|
||||
name: filter
|
||||
in: query
|
||||
description: Filter for just the entities defined by this filter.
|
||||
description: |
|
||||
You can pass in one or more filter sets that get matched against each entity.
|
||||
Each filter set is a number of conditions that all have to match for the
|
||||
condition to be true (conditions effectively have an AND between them). At least
|
||||
one filter set has to be true for the entity to be part of the result set
|
||||
(filter sets effectively have an OR between them).
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
/entities/by-query?filter=kind=user,metadata.namespace=default&filter=kind=group,spec.type
|
||||
|
||||
Return entities that match
|
||||
|
||||
Filter set 1:
|
||||
Condition 1: kind = user
|
||||
AND
|
||||
Condition 2: metadata.namespace = default
|
||||
|
||||
OR
|
||||
|
||||
Filter set 2:
|
||||
Condition 1: kind = group
|
||||
AND
|
||||
Condition 2: spec.type exists
|
||||
```
|
||||
|
||||
Each condition is either on the form `<key>`, or on the form `<key>=<value>`.
|
||||
The first form asserts on the existence of a certain key (with any value), and
|
||||
the second asserts that the key exists and has a certain value. All checks are
|
||||
always case _insensitive_.
|
||||
|
||||
In all cases, the key is a simplified JSON path in a given piece of entity data.
|
||||
Each part of the path is a key of an object, and the traversal also descends
|
||||
through arrays. There are two special forms:
|
||||
|
||||
- Array items that are simple value types (such as strings) match on a key-value
|
||||
pair where the key is the item as a string, and the value is the string `true`
|
||||
- Relations can be matched on a `relations.<type>=<targetRef>` form
|
||||
|
||||
Let's look at a simplified example to illustrate the concept:
|
||||
|
||||
```json
|
||||
{
|
||||
"a": {
|
||||
"b": ["c", { "d": 1 }],
|
||||
"e": 7
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This would match any one of the following conditions:
|
||||
|
||||
- `a`
|
||||
- `a.b`
|
||||
- `a.b.c`
|
||||
- `a.b.c=true`
|
||||
- `a.b.d`
|
||||
- `a.b.d=1`
|
||||
- `a.e`
|
||||
- `a.e=7`
|
||||
|
||||
Some more real world usable examples:
|
||||
|
||||
- Return all orphaned entities:
|
||||
|
||||
`/entities/by-query?filter=metadata.annotations.backstage.io/orphan=true`
|
||||
|
||||
- Return all users and groups:
|
||||
|
||||
`/entities/by-query?filter=kind=user&filter=kind=group`
|
||||
|
||||
- Return all service components:
|
||||
|
||||
`/entities/by-query?filter=kind=component,spec.type=service`
|
||||
|
||||
- Return all entities with the `java` tag:
|
||||
|
||||
`/entities/by-query?filter=metadata.tags.java`
|
||||
|
||||
- Return all users who are members of the `ops` group (note that the full
|
||||
[reference](references.md) of the group is used):
|
||||
|
||||
`/entities/by-query?filter=kind=user,relations.memberof=group:default/ops`
|
||||
|
||||
required: false
|
||||
allowReserved: true
|
||||
schema:
|
||||
@@ -118,7 +259,16 @@ components:
|
||||
orderField:
|
||||
name: orderField
|
||||
in: query
|
||||
description: The fields to sort returned results by.
|
||||
description: |
|
||||
By default the entities are returned ordered by their internal uid. You can
|
||||
customize the `orderField` query parameters to affect that ordering.
|
||||
|
||||
For example, to return entities by their name:
|
||||
|
||||
`/entities/by-query?orderField=metadata.name,asc`
|
||||
|
||||
Each parameter can be followed by `asc` for ascending lexicographical order or
|
||||
`desc` for descending (reverse) lexicographical order.
|
||||
required: false
|
||||
allowReserved: true
|
||||
schema:
|
||||
@@ -674,6 +824,8 @@ paths:
|
||||
/refresh:
|
||||
post:
|
||||
operationId: RefreshEntity
|
||||
tags:
|
||||
- Entity
|
||||
description: Refresh the entity related to entityRef.
|
||||
responses:
|
||||
'200':
|
||||
@@ -705,6 +857,8 @@ paths:
|
||||
/entities:
|
||||
get:
|
||||
operationId: GetEntities
|
||||
tags:
|
||||
- Entity
|
||||
description: Get all entities matching a given filter.
|
||||
responses:
|
||||
'200':
|
||||
@@ -739,6 +893,8 @@ paths:
|
||||
/entities/by-uid/{uid}:
|
||||
get:
|
||||
operationId: GetEntityByUid
|
||||
tags:
|
||||
- Entity
|
||||
description: Get a single entity by the UID.
|
||||
responses:
|
||||
'200':
|
||||
@@ -758,6 +914,8 @@ paths:
|
||||
- $ref: '#/components/parameters/uid'
|
||||
delete:
|
||||
operationId: DeleteEntityByUid
|
||||
tags:
|
||||
- Entity
|
||||
description: Delete a single entity by UID.
|
||||
responses:
|
||||
'204':
|
||||
@@ -774,6 +932,8 @@ paths:
|
||||
/entities/by-name/{kind}/{namespace}/{name}:
|
||||
get:
|
||||
operationId: GetEntityByName
|
||||
tags:
|
||||
- Entity
|
||||
description: Get an entity by an entity ref.
|
||||
responses:
|
||||
'200':
|
||||
@@ -796,6 +956,8 @@ paths:
|
||||
/entities/by-name/{kind}/{namespace}/{name}/ancestry:
|
||||
get:
|
||||
operationId: GetEntityAncestryByName
|
||||
tags:
|
||||
- Entity
|
||||
description: Get an entity's ancestry by entity ref.
|
||||
responses:
|
||||
'200':
|
||||
@@ -818,6 +980,8 @@ paths:
|
||||
/entities/by-refs:
|
||||
post:
|
||||
operationId: GetEntitiesByRefs
|
||||
tags:
|
||||
- Entity
|
||||
description: Get a batch set of entities given an array of entityRefs.
|
||||
responses:
|
||||
'200':
|
||||
@@ -867,6 +1031,8 @@ paths:
|
||||
/entities/by-query:
|
||||
get:
|
||||
operationId: GetEntitiesByQuery
|
||||
tags:
|
||||
- Entity
|
||||
description: Search for entities by a given query.
|
||||
responses:
|
||||
'200':
|
||||
@@ -910,6 +1076,8 @@ paths:
|
||||
/entity-facets:
|
||||
get:
|
||||
operationId: GetEntityFacets
|
||||
tags:
|
||||
- Entity
|
||||
description: Get all entity facets that match the given filters.
|
||||
responses:
|
||||
'200':
|
||||
@@ -945,6 +1113,8 @@ paths:
|
||||
/locations:
|
||||
post:
|
||||
operationId: CreateLocation
|
||||
tags:
|
||||
- Locations
|
||||
description: Create a location for a given target.
|
||||
responses:
|
||||
'201':
|
||||
@@ -995,6 +1165,8 @@ paths:
|
||||
- type
|
||||
get:
|
||||
operationId: GetLocations
|
||||
tags:
|
||||
- Locations
|
||||
description: Get all locations
|
||||
responses:
|
||||
'200':
|
||||
@@ -1019,6 +1191,8 @@ paths:
|
||||
/locations/{id}:
|
||||
get:
|
||||
operationId: GetLocation
|
||||
tags:
|
||||
- Locations
|
||||
description: Get a location by id.
|
||||
responses:
|
||||
'200':
|
||||
@@ -1041,6 +1215,8 @@ paths:
|
||||
type: string
|
||||
delete:
|
||||
operationId: DeleteLocation
|
||||
tags:
|
||||
- Locations
|
||||
description: Delete a location by id.
|
||||
responses:
|
||||
'204':
|
||||
@@ -1062,6 +1238,8 @@ paths:
|
||||
/locations/by-entity/{kind}/{namespace}/{name}:
|
||||
get:
|
||||
operationId: getLocationByEntity
|
||||
tags:
|
||||
- Locations
|
||||
description: Get a location for entity.
|
||||
responses:
|
||||
'200':
|
||||
@@ -1097,6 +1275,8 @@ paths:
|
||||
/analyze-location:
|
||||
post:
|
||||
operationId: AnalyzeLocation
|
||||
tags:
|
||||
- Locations
|
||||
description: Validate a given location.
|
||||
responses:
|
||||
'200':
|
||||
@@ -1129,6 +1309,8 @@ paths:
|
||||
/validate-entity:
|
||||
post:
|
||||
operationId: ValidateEntity
|
||||
tags:
|
||||
- Entity
|
||||
description: Validate that a passed in entity has no errors in schema.
|
||||
responses:
|
||||
'200':
|
||||
|
||||
Reference in New Issue
Block a user