From 4f52bb6ba5b53ea71b68cc2887f5d3cfc181c6f3 Mon Sep 17 00:00:00 2001 From: Aramis Date: Fri, 8 Dec 2023 12:22:32 -0500 Subject: [PATCH 1/7] add an initial tutorial Signed-off-by: Aramis --- docs/openapi/01-getting-started.md | 109 +++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 docs/openapi/01-getting-started.md diff --git a/docs/openapi/01-getting-started.md b/docs/openapi/01-getting-started.md new file mode 100644 index 0000000000..b1a6e0c224 --- /dev/null +++ b/docs/openapi/01-getting-started.md @@ -0,0 +1,109 @@ +# Getting started with OpenAPI in Backstage plugins + +Target Audience: Plugin developers + +Difficulty: Medium + +## Goal + +The goal of this tutorial is to more tightly couple your OpenAPI specification and plugin lifecycle. The OpenAPI tooling project area has created tools that allow you to create, + +1. A typed `express` router that provides strong guardrails during development for input and output values. Support for query, path parameters and request body, as well as experimental support for headers and cookies. +2. An autogenerated client to interact with your plugin's backend. Support for all request types, parameters and body, as well as return types. Provides a low-level interface to allow more customization by higher level libraries. +3. Validation and verifaction tooling to ensure your API and specification stay in sync. Includes testing against your unit tests. + +## Prerequisites + +### Technical Knowledge + +This tutorial assumes that you're already familiar with the following, + +1. How to build a Backstage plugin. +2. `Express.js` and Typescript +3. OpenAPI 3.0 schemas + +### Setting up + +There are two required npm packages before we start, + +1. `@backstage/repo-tools`, this package contains all OpenAPI related commands for your plugins. We will be using this throughout the tutorial. +2. `@opticdev/optic`, this package is a dependency of `@backstage/repo-tools` but is only required for OpenAPI related commands. + +You should install both of the above packages in the _root_ of your workspace. + +## Storing your OpenAPI specification + +You should create a new folder, `src/schema` in your backend plugin to store your OpenAPI (and any other) specifications. For example, if you're adding a specification to the catalog plugin, you would add a `src/schema` folder to `plugins/catalog-backend`, making a `plugins/catalog-backend/src/schema` directory. This directory should have an `openapi.yaml` file inside. + +> Currently, only the `.yaml` ending is supported, not `.yml`. + +## Generating a typed express router from a spec + +Run `yarn backstage-repo-tools schema openapi generate `. This will create an `openapi.generated.ts` file in the `src/schema` directory that contains the OpenAPI schema as well as a generated express router with types. + +Use it like so, update your `router.ts` or `createRouter.ts` file with the following content, + +```diff ++ import { createOpenApiRouter } from '../schema/openapi.generated'; +- import Router from 'express-promise-router'; + +... +export async function createRouter( + options: RouterOptions, +): Promise { ++ const router = await createOpenApiRouter(); +- const router = Router(); +``` + +## Generating a typed client from a spec + +Run `yarn backstage-repo-tools schema openapi generate-client --input-spec /src/schema/openapi.yaml --output-directory `. `` should match the same backend plugin we've been using so far. `` is a new directory and npm package that you should create. The general pattern is `plugins/-client`. + +The generated client will have a directory `src/generated` that exports a `DefaultApiClient` class and all generated types. You can use the client like so, + +```diff ++ import { DefaultApiClient } from './generated'; + +export class CatalogClient implements CatalogApi { ++ private readonly apiClient: DefaultApiClient; + + constructor(options: { + discoveryApi: { getBaseUrl(pluginId: string): Promise }; + fetchApi?: { fetch: typeof fetch }; + }) { ++ this.apiClient = new DefaultApiClient(options); + } + ... +``` + +usage of the types will depend on your type names. + +You should be able to use the generated `DefaultApi.client.ts` file out of the box for your API needs. For full customization, you can use a wrapper around the generated client to adjust the flavor of your clients. + +For more information, see [the docs](./generate-client.md). + +## Validating your spec with test traffic + +Add the following lines to your `createRouter.test.ts` or `router.test.ts` file, + +```diff ++ import { wrapInOpenApiTestServer } from '@backstage/backend-openapi-utils'; ++ import { Server } from 'http'; + +... + +describe('createRouter', () => { +- let app: express.Express; ++ let app: express.Express | Server; + +... + +- app = express().use(router); ++ app = wrapInOpenApiTestServer(express().use(router)); +``` + +This adds a wrapper around the express server that allows it to reroute traffic for `supertest`. Run `yarn backstage-repo-tools schema openapi init` to create some required config files. Now, when you run `yarn backstage-repo-tools schema openapi test` your schema will now be tested against your test data. Any errors will be reported. + +Our command is a small wrapper over [`Optic`](https://github.com/opticdev/optic) which does all of the heavy lifting. + +For more information, see [the docs](./test-case-validation.md). From ec7b27def50455ce2dfbf83c1bf7d65c0cda472f Mon Sep 17 00:00:00 2001 From: Aramis Date: Fri, 8 Dec 2023 12:27:56 -0500 Subject: [PATCH 2/7] fix typos Signed-off-by: Aramis --- docs/openapi/01-getting-started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/openapi/01-getting-started.md b/docs/openapi/01-getting-started.md index b1a6e0c224..4e4c466572 100644 --- a/docs/openapi/01-getting-started.md +++ b/docs/openapi/01-getting-started.md @@ -9,8 +9,8 @@ Difficulty: Medium The goal of this tutorial is to more tightly couple your OpenAPI specification and plugin lifecycle. The OpenAPI tooling project area has created tools that allow you to create, 1. A typed `express` router that provides strong guardrails during development for input and output values. Support for query, path parameters and request body, as well as experimental support for headers and cookies. -2. An autogenerated client to interact with your plugin's backend. Support for all request types, parameters and body, as well as return types. Provides a low-level interface to allow more customization by higher level libraries. -3. Validation and verifaction tooling to ensure your API and specification stay in sync. Includes testing against your unit tests. +2. An auto-generated client to interact with your plugin's backend. Support for all request types, parameters and body, as well as return types. Provides a low-level interface to allow more customization by higher level libraries. +3. Validation and verification tooling to ensure your API and specification stay in sync. Includes testing against your unit tests. ## Prerequisites From dcd782bdc8e36329c2bea2f0b338a36e8fc84d3f Mon Sep 17 00:00:00 2001 From: Aramis Date: Mon, 11 Dec 2023 09:27:03 -0500 Subject: [PATCH 3/7] add tutorial to sidebar and fix wording Signed-off-by: Aramis --- docs/openapi/01-getting-started.md | 14 ++++++++++---- microsite/sidebars.json | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/openapi/01-getting-started.md b/docs/openapi/01-getting-started.md index 4e4c466572..9750192675 100644 --- a/docs/openapi/01-getting-started.md +++ b/docs/openapi/01-getting-started.md @@ -1,4 +1,10 @@ -# Getting started with OpenAPI in Backstage plugins +--- +id: 01-getting-started +title: Creating plugins with OpenAPI (Experimental) +description: Tutorial on how to start using OpenAPI schema-first development in your plugins. +--- + +# Getting started with OpenAPI in your Backstage plugins Target Audience: Plugin developers @@ -6,7 +12,7 @@ Difficulty: Medium ## Goal -The goal of this tutorial is to more tightly couple your OpenAPI specification and plugin lifecycle. The OpenAPI tooling project area has created tools that allow you to create, +The goal of this tutorial is to give you exposure to tools that more tightly couple your OpenAPI specification and plugin lifecycle. The tools we'll be presenting were created by the OpenAPI tooling project area and allow you to create, 1. A typed `express` router that provides strong guardrails during development for input and output values. Support for query, path parameters and request body, as well as experimental support for headers and cookies. 2. An auto-generated client to interact with your plugin's backend. Support for all request types, parameters and body, as well as return types. Provides a low-level interface to allow more customization by higher level libraries. @@ -19,7 +25,7 @@ The goal of this tutorial is to more tightly couple your OpenAPI specification a This tutorial assumes that you're already familiar with the following, 1. How to build a Backstage plugin. -2. `Express.js` and Typescript +2. `Express.js` and `Typescript` 3. OpenAPI 3.0 schemas ### Setting up @@ -35,7 +41,7 @@ You should install both of the above packages in the _root_ of your workspace. You should create a new folder, `src/schema` in your backend plugin to store your OpenAPI (and any other) specifications. For example, if you're adding a specification to the catalog plugin, you would add a `src/schema` folder to `plugins/catalog-backend`, making a `plugins/catalog-backend/src/schema` directory. This directory should have an `openapi.yaml` file inside. -> Currently, only the `.yaml` ending is supported, not `.yml`. +> Currently, only the `.yaml` extension is supported, not `.yml`. ## Generating a typed express router from a spec diff --git a/microsite/sidebars.json b/microsite/sidebars.json index fb513697cb..620e341bac 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -248,6 +248,7 @@ "plugins/internationalization", "plugins/analytics", "plugins/feature-flags", + "openapi/01-getting-started", { "type": "category", "label": "Backends and APIs", From a4bde21af17a6c072df49b8fa49d8f293ebc5b9b Mon Sep 17 00:00:00 2001 From: Aramis Date: Mon, 11 Dec 2023 09:34:00 -0500 Subject: [PATCH 4/7] add a section for openapi Signed-off-by: Aramis --- docs/openapi/01-getting-started.md | 2 +- microsite/sidebars.json | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/openapi/01-getting-started.md b/docs/openapi/01-getting-started.md index 9750192675..eb3b1c1209 100644 --- a/docs/openapi/01-getting-started.md +++ b/docs/openapi/01-getting-started.md @@ -1,6 +1,6 @@ --- id: 01-getting-started -title: Creating plugins with OpenAPI (Experimental) +title: Schema-first plugins with OpenAPI (Experimental) description: Tutorial on how to start using OpenAPI schema-first development in your plugins. --- diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 620e341bac..ec224c59e3 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -248,7 +248,13 @@ "plugins/internationalization", "plugins/analytics", "plugins/feature-flags", - "openapi/01-getting-started", + { + "type": "category", + "label": "OpenAPI", + "items": [ + "openapi/01-getting-started" + ] + }, { "type": "category", "label": "Backends and APIs", From f57cfb2724d6bd2957b27657a906453274f2bc00 Mon Sep 17 00:00:00 2001 From: Aramis Date: Mon, 11 Dec 2023 09:40:16 -0500 Subject: [PATCH 5/7] add other docs to the sidebar as well Signed-off-by: Aramis --- docs/openapi/generate-client.md | 6 ++++++ docs/openapi/test-case-validation.md | 6 ++++++ microsite/sidebars.json | 4 +++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/openapi/generate-client.md b/docs/openapi/generate-client.md index 10e6ec3b2f..4afa7d64d7 100644 --- a/docs/openapi/generate-client.md +++ b/docs/openapi/generate-client.md @@ -1,3 +1,9 @@ +--- +id: generate-client +title: Generate a client from your OpenAPI spec +description: Documentation on how to create a client for a given OpenAPI spec +--- + ## How to generate a client with `repo-tools schema openapi generate-client`? ### Prerequisites diff --git a/docs/openapi/test-case-validation.md b/docs/openapi/test-case-validation.md index 50686b5ba0..84f2f53040 100644 --- a/docs/openapi/test-case-validation.md +++ b/docs/openapi/test-case-validation.md @@ -1,3 +1,9 @@ +--- +id: test-case-validation +title: Validate your OpenAPI spec against test data +description: Documetnation on how to use the `schema openapi test` command. +--- + ## OpenAPI Validation using Test Cases This is primarily performed by `backstage-repo-tools schema openapi test`. Any errors found in the generated specs can be either diff --git a/microsite/sidebars.json b/microsite/sidebars.json index ec224c59e3..380bd4514e 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -252,7 +252,9 @@ "type": "category", "label": "OpenAPI", "items": [ - "openapi/01-getting-started" + "openapi/01-getting-started", + "openapi/generate-client", + "openapi/test-case-validation" ] }, { From 079ff13f3e9f321cdd79a2a76c427f9d7c9173db Mon Sep 17 00:00:00 2001 From: Aramis Date: Wed, 13 Dec 2023 13:49:07 -0500 Subject: [PATCH 6/7] add to the mkdocs file as well Signed-off-by: Aramis --- docs/openapi/test-case-validation.md | 2 +- mkdocs.yml | 4 ++++ package.json | 1 - 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/openapi/test-case-validation.md b/docs/openapi/test-case-validation.md index 84f2f53040..0d93b6d5b5 100644 --- a/docs/openapi/test-case-validation.md +++ b/docs/openapi/test-case-validation.md @@ -1,7 +1,7 @@ --- id: test-case-validation title: Validate your OpenAPI spec against test data -description: Documetnation on how to use the `schema openapi test` command. +description: Documentation on how to use the `schema openapi test` command. --- ## OpenAPI Validation using Test Cases diff --git a/mkdocs.yml b/mkdocs.yml index d4735c63fc..bc6c962df7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -128,6 +128,10 @@ nav: - Composability System: 'plugins/composability.md' - Plugin Analytics: 'plugins/analytics.md' - Feature Flags: 'plugins/feature-flags.md' + - OpenAPI: + - Schema-first plugins with OpenAPI (Experimental): 'openapi/01-getting-started.md' + - Generate a client from your OpenAPI spec: 'openapi/generate-client.md' + - Validate your OpenAPI spec against test data: 'openapi/test-case-validation.md' - Backends and APIs: - Proxying: 'plugins/proxying.md' - Backend plugin: 'plugins/backend-plugin.md' diff --git a/package.json b/package.json index c19dea9d3b..b62758ebd2 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ "snyk:test": "npx snyk test --yarn-workspaces --strict-out-of-sync=false", "snyk:test:package": "yarn snyk:test --include", "build-storybook": "yarn ./storybook run build-storybook", - "techdocs-cli": "node scripts/techdocs-cli.js", "techdocs-cli:dev": "cross-env TECHDOCS_CLI_DEV_MODE=true node scripts/techdocs-cli.js", "prepare": "husky install", "postinstall": "husky install || true" From 861dea67cafaccf6385d09444a128744d9b642b2 Mon Sep 17 00:00:00 2001 From: Aramis Date: Wed, 13 Dec 2023 13:49:30 -0500 Subject: [PATCH 7/7] revert package.json changes Signed-off-by: Aramis --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index b62758ebd2..c19dea9d3b 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "snyk:test": "npx snyk test --yarn-workspaces --strict-out-of-sync=false", "snyk:test:package": "yarn snyk:test --include", "build-storybook": "yarn ./storybook run build-storybook", + "techdocs-cli": "node scripts/techdocs-cli.js", "techdocs-cli:dev": "cross-env TECHDOCS_CLI_DEV_MODE=true node scripts/techdocs-cli.js", "prepare": "husky install", "postinstall": "husky install || true"