diff --git a/.changeset/plenty-mails-do.md b/.changeset/plenty-mails-do.md new file mode 100644 index 0000000000..6a9e815ebb --- /dev/null +++ b/.changeset/plenty-mails-do.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-api-docs': patch +--- + +Added `resolvers` prop to `AsyncApiDefinitionWidget`. This allows to override the default http/https resolvers, for example to add authentication to requests to internal schema registries. diff --git a/plugins/api-docs/README-alpha.md b/plugins/api-docs/README-alpha.md index 676a2c66c1..eb84ef8c27 100644 --- a/plugins/api-docs/README-alpha.md +++ b/plugins/api-docs/README-alpha.md @@ -50,6 +50,7 @@ To link that a component provides or consumes an API, see the [`providesApis`](h - [Custom Api Renderings](#custom-api-renderings) - [Adding Swagger UI Interceptor](#adding-requestinterceptor-to-swagger-ui) - [Providing Swagger UI Specific Supported Methods](#provide-specific-supported-methods-to-swagger-ui) + - [Custom Resolvers for AsyncApi](#custom-resolvers-for-asyncapi) - [Integrations](#integrations) - [Implementing OAuth 2 Authorization Code flow with Swagger UI](#implementing-oauth-2-authorization-code-flow-with-swagger-ui) @@ -1092,6 +1093,61 @@ export default createExtensionOverrides({ N.B. if you wish to disable the `Try It Out` feature for your API, you can provide an empty list to the `supportedSubmitMethods` parameter. +##### Custom Resolvers for AsyncApi + +You can override the default http/https resolvers, for example to add authentication to requests to internal schema registries by providing the `resolvers` prop to the `AsyncApiDefinitionWidget`. This is an example: + +```tsx +... +import { + AsyncApiDefinitionWidget, + apiDocsConfigRef, + defaultDefinitionWidgets, +} from '@backstage/plugin-api-docs'; +import { ApiEntity } from '@backstage/catalog-model'; + +export const apis: AnyApiFactory[] = [ +... + createApiFactory({ + api: apiDocsConfigRef, + deps: {}, + factory: () => { + const myCustomResolver = { + schema: 'https', + order: 1, + canRead: true, + async read(uri: any) { + const response = await fetch(request, { + headers: { + X-Custom: 'Custom', + }, + }); + return response.text(); + }, + }; + + const definitionWidgets = defaultDefinitionWidgets().map(obj => { + if (obj.type === 'asyncapi') { + return { + ...obj, + component: (definition) => ( + + ), + }; + } + return obj; + }); + + return { + getApiDefinitionWidget: (apiEntity: ApiEntity) => { + return definitionWidgets.find(d => d.type === apiEntity.spec.type); + }, + }; + } + }) +] +``` + ### Integrations #### Implementing OAuth 2 Authorization Code flow with Swagger UI diff --git a/plugins/api-docs/README.md b/plugins/api-docs/README.md index 2387752cc9..2738cc09ae 100644 --- a/plugins/api-docs/README.md +++ b/plugins/api-docs/README.md @@ -222,10 +222,6 @@ security: - [read_pets, write_pets] ``` -## Links - -- [The Backstage homepage](https://backstage.io) - ### Adding `requestInterceptor` to Swagger UI To configure a [`requestInterceptor` for Swagger UI](https://github.com/swagger-api/swagger-ui/tree/master/flavors/swagger-ui-react#requestinterceptor-proptypesfunc) you'll need to add the following to your `api.tsx`: @@ -319,3 +315,62 @@ export const apis: AnyApiFactory[] = [ N.B. if you wish to disable the `Try It Out` feature for your API, you can provide an empty list to the `supportedSubmitMethods` parameter. + +### Custom Resolvers for AsyncApi + +You can override the default http/https resolvers, for example to add authentication to requests to internal schema registries by providing the `resolvers` prop to the `AsyncApiDefinitionWidget`. This is an example: + +```tsx +... +import { + AsyncApiDefinitionWidget, + apiDocsConfigRef, + defaultDefinitionWidgets, +} from '@backstage/plugin-api-docs'; +import { ApiEntity } from '@backstage/catalog-model'; + +export const apis: AnyApiFactory[] = [ +... + createApiFactory({ + api: apiDocsConfigRef, + deps: {}, + factory: () => { + const myCustomResolver = { + schema: 'https', + order: 1, + canRead: true, + async read(uri: any) { + const response = await fetch(request, { + headers: { + X-Custom: 'Custom', + }, + }); + return response.text(); + }, + }; + + const definitionWidgets = defaultDefinitionWidgets().map(obj => { + if (obj.type === 'asyncapi') { + return { + ...obj, + component: (definition) => ( + + ), + }; + } + return obj; + }); + + return { + getApiDefinitionWidget: (apiEntity: ApiEntity) => { + return definitionWidgets.find(d => d.type === apiEntity.spec.type); + }, + }; + } + }) +] +``` + +## Links + +- [The Backstage homepage](https://backstage.io) diff --git a/plugins/api-docs/api-report.md b/plugins/api-docs/api-report.md index b3d82fc991..3c3f245a3c 100644 --- a/plugins/api-docs/api-report.md +++ b/plugins/api-docs/api-report.md @@ -85,6 +85,15 @@ export const AsyncApiDefinitionWidget: ( // @public (undocumented) export type AsyncApiDefinitionWidgetProps = { definition: string; + resolvers?: AsyncApiResolver[]; +}; + +// @public (undocumented) +export type AsyncApiResolver = { + schema: string; + order: number; + canRead: boolean; + read(uri: any): Promise; }; // @public (undocumented) diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx index 85ce15132a..4817774182 100644 --- a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx +++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx @@ -20,6 +20,14 @@ import { makeStyles, alpha, darken } from '@material-ui/core/styles'; import React from 'react'; import { useTheme } from '@material-ui/core/styles'; +/** @public */ +export type AsyncApiResolver = { + schema: string; + order: number; + canRead: boolean; + read(uri: any): Promise; +}; + const useStyles = makeStyles(theme => ({ root: { fontFamily: 'inherit', @@ -143,7 +151,7 @@ const useStyles = makeStyles(theme => ({ }, })); -const httpsFetchResolver = { +const httpsFetchResolver: AsyncApiResolver = { schema: 'https', order: 1, canRead: true, @@ -153,7 +161,7 @@ const httpsFetchResolver = { }, }; -const httpFetchResolver = { +const httpFetchResolver: AsyncApiResolver = { schema: 'http', order: 1, canRead: true, @@ -163,27 +171,36 @@ const httpFetchResolver = { }, }; -const config = { - parserOptions: { - __unstable: { - resolver: { - resolvers: [httpsFetchResolver, httpFetchResolver], - }, - }, - }, -}; - type Props = { definition: string; + resolvers?: AsyncApiResolver[]; }; -export const AsyncApiDefinition = ({ definition }: Props): JSX.Element => { +export const AsyncApiDefinition = ({ + definition, + resolvers, +}: Props): JSX.Element => { const classes = useStyles(); const theme = useTheme(); const classNames = `${classes.root} ${ theme.palette.type === 'dark' ? classes.dark : '' }`; + const config = { + parserOptions: { + __unstable: { + resolver: { + resolvers: [httpsFetchResolver, httpFetchResolver], + }, + }, + }, + }; + + // Overwrite default resolvers if custom ones are set + if (resolvers) { + config.parserOptions.__unstable.resolver.resolvers = resolvers; + } + return (
diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx index 2e8710b008..104a55e576 100644 --- a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx +++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx @@ -16,6 +16,7 @@ import { Progress } from '@backstage/core-components'; import React, { Suspense } from 'react'; +import { AsyncApiResolver } from './AsyncApiDefinition'; // The asyncapi component and related CSS has a significant size, only load it // if the element is actually used. @@ -28,6 +29,7 @@ const LazyAsyncApiDefinition = React.lazy(() => /** @public */ export type AsyncApiDefinitionWidgetProps = { definition: string; + resolvers?: AsyncApiResolver[]; }; /** @public */ diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/index.ts b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/index.ts index 7cc49e3138..3b07c32cfe 100644 --- a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/index.ts +++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/index.ts @@ -16,3 +16,4 @@ export { AsyncApiDefinitionWidget } from './AsyncApiDefinitionWidget'; export type { AsyncApiDefinitionWidgetProps } from './AsyncApiDefinitionWidget'; +export type { AsyncApiResolver } from './AsyncApiDefinition';