Merge pull request #12600 from unredundant/plugin-apollo-explorer

apollo explorer plugin
This commit is contained in:
Ben Lambert
2022-07-18 18:06:05 +02:00
committed by GitHub
21 changed files with 494 additions and 3 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-apollo-explorer': minor
---
Apollo Explorer plugin now available! Installation instructions can be found in the plugin README
+3 -3
View File
@@ -49,18 +49,18 @@
"@backstage/plugin-rollbar": "^0.4.7-next.3",
"@backstage/plugin-scaffolder": "^1.4.0-next.3",
"@backstage/plugin-search": "^0.9.1-next.3",
"@backstage/plugin-search-react": "^0.2.2-next.3",
"@backstage/plugin-search-common": "^0.3.6-next.0",
"@backstage/plugin-search-react": "^0.2.2-next.3",
"@backstage/plugin-sentry": "^0.4.0-next.3",
"@backstage/plugin-shortcuts": "^0.2.8-next.3",
"@backstage/plugin-stack-overflow": "^0.1.3-next.3",
"@backstage/plugin-tech-insights": "^0.2.3-next.3",
"@backstage/plugin-tech-radar": "^0.5.14-next.3",
"@backstage/plugin-techdocs": "^1.2.1-next.3",
"@backstage/plugin-techdocs-react": "^1.0.2-next.2",
"@backstage/plugin-techdocs-module-addons-contrib": "^1.0.2-next.3",
"@backstage/plugin-techdocs-react": "^1.0.2-next.2",
"@backstage/plugin-todo": "^0.2.9-next.3",
"@backstage/plugin-user-settings": "^0.4.6-next.3",
"@backstage/plugin-tech-insights": "^0.2.3-next.3",
"@backstage/theme": "^0.2.16-next.1",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
+1
View File
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
+66
View File
@@ -0,0 +1,66 @@
# apollo-explorer
Welcome to the Apollo Explorer plugin!
This plugin allows users to directly embed an [Apollo](https://www.apollographql.com) graph explorer directly into
Backstage!
## Getting started
### Getting an Apollo Graph Reference
First things first, you will need an Apollo account, and a graph imported into your account. This is beyond the scope of
this plugin, so if you are totally new to Apollo, please reference their official
documentation [here](https://www.apollographql.com/docs).
Once you have a graph set up in Apollo, we need to grab the graph reference. First, go to your Apollo graphs home page and choose the graph you wish to embed.
![Apollo Graph List](./docs/img/apollo-graph-list.png)
Once you are in your graph explorer, click the dropdown next to the share icon and select `Share as Embedded`
![Share as Embedded](./docs/img/share-as-embedded.png)
This modal contains a number of useful properties, all of which can be passed to the plugin via the component properties, but the only mandatory input we need from here is the `graphRef`.
![Graph Ref](./docs/img/graph-ref.png)
Hold on to this snippet for a second while we set up the plugin ✨
### Installing the Backstage Plugin
First, add the plugin to your Backstage app
```shell
yarn --cwd packages/app add @backstage/plugin-apollo-explorer
```
Then, in `packages/app/src/App.tsx` add the plugin as a `Route`
```typescript
<Route
path="/apollo-explorer"
element={
<ApolloExplorerPage
endpoints={[
{ title: 'Github', graphRef: 'my-github-graph-ref@current' },
{ title: 'Linear', graphRef: 'my-linear-graph-ref@current' },
]}
/>
}
/>
```
Then, in `packages/app/src/components/Root/Root.tsx` add a sidebar item so users can find your beautiful plugin!
```typescript
<SidebarItem icon={GraphiQLIcon} to="apollo-explorer" text="Apollo Explorer" />
```
That's it! You should now see an `Apollo Explorer` item in your sidebar, and if you click it, you should see your graph(s) load and direct you to authenticate via Apollo!
![Needs Auth](./docs/img/needs-auth.png)
Once you authenticate, your graph is ready to use 🚀
![Logged In](./docs/img/logged-in.png)
+48
View File
@@ -0,0 +1,48 @@
## API Report File for "@backstage/plugin-apollo-explorer"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
/// <reference types="react" />
import { BackstagePlugin } from '@backstage/core-plugin-api';
import { JSONObject } from '@apollo/explorer/src/helpers/types';
import { RouteRef } from '@backstage/core-plugin-api';
// @public
export const ApolloExplorerPage: ({
title,
subtitle,
endpoints,
}: {
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;
}[];
}) => JSX.Element;
// @public
export const apolloExplorerPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{}
>;
// (No @packageDocumentation comment for this package)
```
+27
View File
@@ -0,0 +1,27 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { createDevApp } from '@backstage/dev-utils';
import { apolloExplorerPlugin, ApolloExplorerPage } from '../src/plugin';
createDevApp()
.registerPlugin(apolloExplorerPlugin)
.addPage({
element: <ApolloExplorerPage endpoints={[]} />,
title: 'Root Page',
path: '/apollo-explorer',
})
.render();
Binary file not shown.

After

Width:  |  Height:  |  Size: 436 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 KiB

+54
View File
@@ -0,0 +1,54 @@
{
"name": "@backstage/plugin-apollo-explorer",
"version": "0.0.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
"types": "dist/index.d.ts"
},
"backstage": {
"role": "frontend-plugin"
},
"scripts": {
"start": "backstage-cli package start",
"build": "backstage-cli package build",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"clean": "backstage-cli package clean",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack"
},
"dependencies": {
"@backstage/core-components": "^0.10.0-next.3",
"@backstage/core-plugin-api": "^1.0.4-next.0",
"@backstage/theme": "^0.2.16-next.1",
"@material-ui/core": "^4.9.13",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.57",
"react-use": "^17.2.4",
"@apollo/explorer": "^1.1.1",
"use-deep-compare-effect": "^1.8.1"
},
"peerDependencies": {
"react": "^16.13.1 || ^17.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.18.0-next.3",
"@backstage/core-app-api": "^1.0.4-next.1",
"@backstage/dev-utils": "^1.0.4-next.3",
"@backstage/test-utils": "^1.1.2-next.2",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^14.0.0",
"@types/jest": "*",
"@types/node": "*",
"msw": "^0.43.0",
"cross-fetch": "^3.1.5"
},
"files": [
"dist"
]
}
@@ -0,0 +1,85 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useState } from 'react';
import { Divider, makeStyles, Tab, Tabs } from '@material-ui/core';
import { BackstageTheme } from '@backstage/theme';
import { JSONObject } from '@apollo/explorer/src/helpers/types';
import { ApolloExplorer } from '@apollo/explorer/react';
import { Content } from '@backstage/core-components';
const useStyles = makeStyles<BackstageTheme>(theme => ({
tabs: {
background: theme.palette.background.paper,
},
root: {
height: '100%',
},
content: {
height: '100%',
},
explorer: {
height: '95%',
},
}));
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: ApolloEndpointProps[];
};
export const ApolloExplorerBrowser = ({ endpoints }: Props) => {
const classes = useStyles();
const [tabIndex, setTabIndex] = useState(0);
return (
<div className={classes.root}>
<Tabs
classes={{ root: classes.tabs }}
value={tabIndex}
onChange={(_, value) => setTabIndex(value)}
indicatorColor="primary"
>
{endpoints.map(({ title }, index) => (
<Tab key={index} label={title} value={index} />
))}
</Tabs>
<Divider />
<Content className={classes.content}>
<ApolloExplorer
className={classes.explorer}
graphRef={endpoints[tabIndex].graphRef}
persistExplorerState={endpoints[tabIndex].persistExplorerState}
initialState={endpoints[tabIndex].initialState}
/>
</Content>
</div>
);
};
@@ -0,0 +1,16 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { ApolloExplorerBrowser } from './ApolloExplorerBrowser';
@@ -0,0 +1,53 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { Content, Header, Page } from '@backstage/core-components';
import { ApolloExplorerBrowser } from '../ApolloExplorerBrowser';
import { JSONObject } from '@apollo/explorer/src/helpers/types';
type EndpointProps = {
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 = {
title?: string | undefined;
subtitle?: string | undefined;
endpoints: EndpointProps[];
};
export const ApolloExplorerPage = ({ title, subtitle, endpoints }: Props) => {
return (
<Page themeId="tool">
<Header title={title ?? 'Apollo Explorer 👩‍🚀'} subtitle={subtitle ?? ''} />
<Content noPadding>
<ApolloExplorerBrowser endpoints={endpoints} />
</Content>
</Page>
);
};
@@ -0,0 +1,16 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { ApolloExplorerPage } from './ApolloExplorerPage';
+16
View File
@@ -0,0 +1,16 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { apolloExplorerPlugin, ApolloExplorerPage } from './plugin';
@@ -0,0 +1,22 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { apolloExplorerPlugin } from './plugin';
describe('apollo-explorer', () => {
it('should export plugin', () => {
expect(apolloExplorerPlugin).toBeDefined();
});
});
+45
View File
@@ -0,0 +1,45 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
createPlugin,
createRoutableExtension,
} from '@backstage/core-plugin-api';
import { rootRouteRef } from './routes';
/**
* Plugin that allows Apollo Explorer instances to be directly embedded into Backstage
* @public
*/
export const apolloExplorerPlugin = createPlugin({
id: 'apollo-explorer',
routes: {
root: rootRouteRef,
},
});
/**
* Main component that wraps the embedded graph(s)
* @public
*/
export const ApolloExplorerPage = apolloExplorerPlugin.provide(
createRoutableExtension({
name: 'ApolloExplorerPage',
component: () =>
import('./components/ApolloExplorerPage').then(m => m.ApolloExplorerPage),
mountPoint: rootRouteRef,
}),
);
+20
View File
@@ -0,0 +1,20 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createRouteRef } from '@backstage/core-plugin-api';
export const rootRouteRef = createRouteRef({
id: 'apollo-explorer',
});
+17
View File
@@ -0,0 +1,17 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import '@testing-library/jest-dom';
import 'cross-fetch/polyfill';