Merge pull request #23405 from AmericanAirlines/feat(apollo-explorer)--allow-callbacks-using-apiholder
feat(apollo explorer): allow callbacks using apiholder
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-apollo-explorer': minor
|
||||
---
|
||||
|
||||
feat(apollo-explorer): allow callbacks using apiholder
|
||||
@@ -69,3 +69,36 @@ That's it! You should now see an `Apollo Explorer` item in your sidebar, and if
|
||||
Once you authenticate, your graph is ready to use 🚀
|
||||
|
||||

|
||||
|
||||
### Authentication Tokens for Apollo Studio
|
||||
|
||||
If you need to utilize an ApiRef to supply a token to Apollo, you may do so using an ApiHolder.
|
||||
|
||||
In `packages/app/src/App.tsx` perform the following modifications from above. The import `ssoAuthApiRef` is used as an example and **does not exist**.
|
||||
|
||||
```typescript
|
||||
import { ApolloExplorerPage, EndpointProps } from '@backstage/plugin-apollo-explorer';
|
||||
import { ssoAuthApiRef } from '@companyxyz/devkit';
|
||||
import { ApiHolder } from '@backstage/core-plugin-api';
|
||||
|
||||
async function authCallback(options: { apiHolder: ApiHolder }): Promise<{token: string}> {
|
||||
const sso = options.apiHolder.get<any>(ssoAuthApiRef)
|
||||
return await sso.getToken()
|
||||
}
|
||||
|
||||
const routes = (
|
||||
<FlatRoutes>
|
||||
{/* other routes... */}
|
||||
<Route
|
||||
path="/apollo-explorer"
|
||||
element={
|
||||
<ApolloExplorerPage
|
||||
endpoints={[{
|
||||
title: 'Github',
|
||||
graphRef: 'my-github-graph-ref@current',
|
||||
authCallback: authCallback
|
||||
}]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
```
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
```ts
|
||||
/// <reference types="react" />
|
||||
|
||||
import { ApiHolder } from '@backstage/core-plugin-api';
|
||||
import { BackstagePlugin } from '@backstage/core-plugin-api';
|
||||
import { JSONObject } from '@apollo/explorer/src/helpers/types';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
@@ -14,23 +15,7 @@ import { RouteRef } from '@backstage/core-plugin-api';
|
||||
export const ApolloExplorerPage: (props: {
|
||||
title?: string | undefined;
|
||||
subtitle?: string | undefined;
|
||||
endpoints: {
|
||||
title: string;
|
||||
graphRef: string;
|
||||
persistExplorerState?: boolean | undefined;
|
||||
initialState?:
|
||||
| {
|
||||
document?: string | undefined;
|
||||
variables?: JSONObject | undefined;
|
||||
headers?: Record<string, string> | undefined;
|
||||
displayOptions: {
|
||||
docsPanelState?: 'closed' | 'open' | undefined;
|
||||
showHeadersAndEnvVars?: boolean | undefined;
|
||||
theme?: 'dark' | 'light' | undefined;
|
||||
};
|
||||
}
|
||||
| undefined;
|
||||
}[];
|
||||
endpoints: EndpointProps[];
|
||||
}) => JSX_2.Element;
|
||||
|
||||
// @public
|
||||
@@ -40,4 +25,27 @@ export const apolloExplorerPlugin: BackstagePlugin<
|
||||
},
|
||||
{}
|
||||
>;
|
||||
|
||||
// @public
|
||||
export type AuthCallback = (options: { apiHolder: ApiHolder }) => Promise<{
|
||||
token: string;
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export type EndpointProps = {
|
||||
title: string;
|
||||
graphRef: string;
|
||||
authCallback?: AuthCallback;
|
||||
persistExplorerState?: boolean;
|
||||
initialState?: {
|
||||
document?: string;
|
||||
variables?: JSONObject;
|
||||
headers?: Record<string, string>;
|
||||
displayOptions: {
|
||||
docsPanelState?: 'open' | 'closed';
|
||||
showHeadersAndEnvVars?: boolean;
|
||||
theme?: 'dark' | 'light';
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
@@ -36,7 +36,9 @@
|
||||
"@backstage/core-components": "workspace:^",
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
"@material-ui/lab": "^4.0.0-alpha.61",
|
||||
"@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
"react-use": "^17.5.0",
|
||||
"use-deep-compare-effect": "^1.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
+33
-17
@@ -19,9 +19,11 @@ import Divider from '@material-ui/core/Divider';
|
||||
import Tab from '@material-ui/core/Tab';
|
||||
import Tabs from '@material-ui/core/Tabs';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { JSONObject } from '@apollo/explorer/src/helpers/types';
|
||||
import { ApolloExplorer } from '@apollo/explorer/react';
|
||||
import { Content } from '@backstage/core-components';
|
||||
import { HandleRequest } from '@apollo/explorer/src/helpers/postMessageRelayHelpers';
|
||||
import { EndpointProps } from '../ApolloExplorerPage';
|
||||
import { useApiHolder } from '@backstage/core-plugin-api';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
tabs: {
|
||||
@@ -38,30 +40,41 @@ const useStyles = makeStyles(theme => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export type ApolloEndpointProps = {
|
||||
title: string;
|
||||
graphRef: string;
|
||||
persistExplorerState?: boolean;
|
||||
initialState?: {
|
||||
document?: string;
|
||||
variables?: JSONObject;
|
||||
headers?: Record<string, string>;
|
||||
displayOptions: {
|
||||
docsPanelState?: 'open' | 'closed';
|
||||
showHeadersAndEnvVars?: boolean;
|
||||
theme?: 'dark' | 'light';
|
||||
};
|
||||
};
|
||||
type Props = {
|
||||
endpoints: EndpointProps[];
|
||||
authCallback?: () => Promise<{ token: string }>;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
endpoints: ApolloEndpointProps[];
|
||||
export const handleAuthRequest = ({
|
||||
authCallback,
|
||||
}: {
|
||||
authCallback: Props['authCallback'];
|
||||
}): HandleRequest => {
|
||||
const handleRequest: HandleRequest = async (endpointUrl, options) =>
|
||||
fetch(endpointUrl, {
|
||||
...options,
|
||||
headers: {
|
||||
...options.headers,
|
||||
...(authCallback && {
|
||||
Authorization: `Bearer ${(await authCallback()).token}`,
|
||||
}),
|
||||
},
|
||||
});
|
||||
return handleRequest;
|
||||
};
|
||||
|
||||
export const ApolloExplorerBrowser = ({ endpoints }: Props) => {
|
||||
const classes = useStyles();
|
||||
const [tabIndex, setTabIndex] = useState(0);
|
||||
|
||||
const apiHolder = useApiHolder();
|
||||
|
||||
const getAuthCallback = (index: number) => {
|
||||
const authCallback = endpoints[index].authCallback;
|
||||
if (authCallback === undefined) return undefined;
|
||||
return () => authCallback({ apiHolder });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Tabs
|
||||
@@ -79,6 +92,9 @@ export const ApolloExplorerBrowser = ({ endpoints }: Props) => {
|
||||
<ApolloExplorer
|
||||
className={classes.explorer}
|
||||
graphRef={endpoints[tabIndex].graphRef}
|
||||
handleRequest={handleAuthRequest({
|
||||
authCallback: getAuthCallback(tabIndex),
|
||||
})}
|
||||
persistExplorerState={endpoints[tabIndex].persistExplorerState}
|
||||
initialState={endpoints[tabIndex].initialState}
|
||||
/>
|
||||
|
||||
@@ -18,10 +18,17 @@ import React from 'react';
|
||||
import { Content, Header, Page } from '@backstage/core-components';
|
||||
import { ApolloExplorerBrowser } from '../ApolloExplorerBrowser';
|
||||
import { JSONObject } from '@apollo/explorer/src/helpers/types';
|
||||
import { ApiHolder } from '@backstage/core-plugin-api';
|
||||
|
||||
type EndpointProps = {
|
||||
/**
|
||||
* Export types to be used with {@link @backstage/apollo-explorer#ApolloExplorerPage}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type EndpointProps = {
|
||||
title: string;
|
||||
graphRef: string;
|
||||
authCallback?: AuthCallback;
|
||||
persistExplorerState?: boolean;
|
||||
initialState?: {
|
||||
document?: string;
|
||||
@@ -35,6 +42,15 @@ type EndpointProps = {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Export types to be used with {@link @backstage/apollo-explorer#ApolloExplorerPage}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AuthCallback = (options: {
|
||||
apiHolder: ApiHolder;
|
||||
}) => Promise<{ token: string }>;
|
||||
|
||||
type Props = {
|
||||
title?: string | undefined;
|
||||
subtitle?: string | undefined;
|
||||
@@ -43,6 +59,7 @@ type Props = {
|
||||
|
||||
export const ApolloExplorerPage = (props: Props) => {
|
||||
const { title, subtitle, endpoints } = props;
|
||||
|
||||
return (
|
||||
<Page themeId="tool">
|
||||
<Header title={title ?? 'Apollo Explorer 👩🚀'} subtitle={subtitle ?? ''} />
|
||||
|
||||
@@ -14,3 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { ApolloExplorerPage } from './ApolloExplorerPage';
|
||||
export type { EndpointProps, AuthCallback } from './ApolloExplorerPage';
|
||||
|
||||
@@ -20,3 +20,7 @@
|
||||
* @packageDocumentation
|
||||
*/
|
||||
export { apolloExplorerPlugin, ApolloExplorerPage } from './plugin';
|
||||
export type {
|
||||
EndpointProps,
|
||||
AuthCallback,
|
||||
} from './components/ApolloExplorerPage';
|
||||
|
||||
@@ -4681,10 +4681,12 @@ __metadata:
|
||||
"@backstage/core-plugin-api": "workspace:^"
|
||||
"@backstage/dev-utils": "workspace:^"
|
||||
"@material-ui/core": ^4.12.2
|
||||
"@material-ui/lab": ^4.0.0-alpha.61
|
||||
"@testing-library/dom": ^9.0.0
|
||||
"@testing-library/jest-dom": ^6.0.0
|
||||
"@testing-library/react": ^15.0.0
|
||||
"@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0
|
||||
react-use: ^17.5.0
|
||||
use-deep-compare-effect: ^1.8.1
|
||||
peerDependencies:
|
||||
react: ^16.13.1 || ^17.0.0 || ^18.0.0
|
||||
@@ -39887,7 +39889,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-use@npm:^17.2.4, react-use@npm:^17.3.1, react-use@npm:^17.3.2, react-use@npm:^17.4.0":
|
||||
"react-use@npm:^17.2.4, react-use@npm:^17.3.1, react-use@npm:^17.3.2, react-use@npm:^17.4.0, react-use@npm:^17.5.0":
|
||||
version: 17.5.0
|
||||
resolution: "react-use@npm:17.5.0"
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user