Merge pull request #25325 from simonstamm/master

This commit is contained in:
Ben Lambert
2024-08-16 11:38:42 +02:00
committed by GitHub
7 changed files with 162 additions and 17 deletions
+5
View File
@@ -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.
+56
View File
@@ -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) => (
<AsyncApiDefinitionWidget definition={definition} resolvers={[myCustomResolver]} />
),
};
}
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
+59 -4
View File
@@ -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) => (
<AsyncApiDefinitionWidget definition={definition} resolvers={[myCustomResolver]} />
),
};
}
return obj;
});
return {
getApiDefinitionWidget: (apiEntity: ApiEntity) => {
return definitionWidgets.find(d => d.type === apiEntity.spec.type);
},
};
}
})
]
```
## Links
- [The Backstage homepage](https://backstage.io)
+9
View File
@@ -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<string>;
};
// @public (undocumented)
@@ -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<string>;
};
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 (
<div className={classNames}>
<AsyncApi schema={definition} config={config} />
@@ -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 */
@@ -16,3 +16,4 @@
export { AsyncApiDefinitionWidget } from './AsyncApiDefinitionWidget';
export type { AsyncApiDefinitionWidgetProps } from './AsyncApiDefinitionWidget';
export type { AsyncApiResolver } from './AsyncApiDefinition';