feat(plugin): Add graphql-voyager as new backstage plugin
Signed-off-by: MitchWijt <mitchel@wijt.net>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
---
|
||||
title: GraphQL Voyager
|
||||
author: PostNL
|
||||
authorUrl: https://github.com/postnl
|
||||
category: Debugging
|
||||
description: Integrates the GraphQL Voyager tool inside Backstage.
|
||||
documentation: https://github.com/backstage/backstage/tree/master/plugins/graphql-voyager
|
||||
iconUrl: https://res.cloudinary.com/apideck/image/upload/v1612724234/icons/graphql-voyager.png
|
||||
npmPackageName: '@backstage/plugin-graphql-voyager'
|
||||
tags:
|
||||
- graphql
|
||||
- graphql-voyager
|
||||
addedDate: '2023-01-27'
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
@@ -0,0 +1,75 @@
|
||||
# graphql-voyager
|
||||
|
||||
Welcome to the graphql-voyager plugin!
|
||||
|
||||
_This plugin was created through the Backstage CLI_
|
||||
|
||||
## Getting started
|
||||
|
||||
### Installing
|
||||
|
||||
To get started, first install the plugin with the following command:
|
||||
|
||||
```bash
|
||||
# From your Backstage root directory
|
||||
yarn add --cwd packages/app @backstage/plugin-graphql-voyager
|
||||
```
|
||||
|
||||
### Adding the page
|
||||
|
||||
In order to be able to navigate to the graphQL voyager page, a new route needs to be added to the app. This can be done in the following way:
|
||||
|
||||
```tsx
|
||||
// packages/app/App.tsx
|
||||
|
||||
import { GraphQLVoyagerPage } from "@backstage/plugin-graphql-voyager";
|
||||
|
||||
const routes = (
|
||||
<FlatRoutes>
|
||||
<Route path="/graphql-voyager" element={<GraphqlVoyagerPage/>}/>
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
In order for the plugin to function correctly. GraphQL endpoints need to be added / configured through the GraphQLVoyager API. This can be done by implementing the `graphQlVoyagerApiRef` exported by this plugin.
|
||||
|
||||
Add your own configuration to the `packages/app/src/apis.ts` file the following way:
|
||||
|
||||
```ts
|
||||
import { identityApiRef } from './IdentityApi';
|
||||
import { GraphQLVoyagerEndpoints } from './GraphQLVoyagerEndpoints';
|
||||
|
||||
export const apis: AnyApiFactory[] = [
|
||||
createApiFactory({
|
||||
api: graphQlVoyagerApiRef,
|
||||
deps: { identityApi: identityApiRef },
|
||||
factory: ({ identityApiRef }) => {
|
||||
GraphQLVoyagerEndpoints.from([
|
||||
{
|
||||
id: `graphql-voyager-endpoint-id`,
|
||||
title: 'endpoint-title',
|
||||
introspectionErrorMessage:
|
||||
'Unable to perform introspection, make sure you are on the correct environment.',
|
||||
introspection: async (query: any) => {
|
||||
const token = 'someSecretJWTComingFromIdentityApiRef';
|
||||
|
||||
const res = await fetch('graphQLEndpoint', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ query }),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: token,
|
||||
},
|
||||
});
|
||||
|
||||
return res.json();
|
||||
},
|
||||
voyagerProps: {
|
||||
hideDocs: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
},
|
||||
}),
|
||||
];
|
||||
```
|
||||
@@ -0,0 +1,64 @@
|
||||
## API Report File for "@backstage/plugin-graphql-voyager"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
/// <reference types="react" />
|
||||
|
||||
import { ApiRef } from '@backstage/core-plugin-api';
|
||||
import { BackstagePlugin } from '@backstage/core-plugin-api';
|
||||
import { RouteRef } from '@backstage/core-plugin-api';
|
||||
import { VoyagerDisplayOptions } from 'graphql-voyager/typings/components/Voyager';
|
||||
import { WorkerCallback } from 'graphql-voyager/typings/utils/types';
|
||||
|
||||
// @public (undocumented)
|
||||
export type GraphQLVoyagerApi = {
|
||||
getEndpoints(): Promise<GraphQLVoyagerEndpoint[]>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export const graphQlVoyagerApiRef: ApiRef<GraphQLVoyagerApi>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type GraphQLVoyagerEndpoint = {
|
||||
id: string;
|
||||
title: string;
|
||||
introspection: (body: any) => Promise<any>;
|
||||
introspectionErrorMessage: string;
|
||||
voyagerProps?: {
|
||||
displayOptions?: VoyagerDisplayOptions;
|
||||
hideDocs?: boolean;
|
||||
hideSettings?: boolean;
|
||||
workerURI?: string;
|
||||
loadWorker?: WorkerCallback;
|
||||
};
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export class GraphQLVoyagerEndpoints implements GraphQLVoyagerApi {
|
||||
// (undocumented)
|
||||
static from(endpoints: GraphQLVoyagerEndpoint[]): GraphQLVoyagerEndpoints;
|
||||
// (undocumented)
|
||||
getEndpoints(): Promise<GraphQLVoyagerEndpoint[]>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const GraphqlVoyagerPage: () => JSX.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export const graphqlVoyagerPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{}
|
||||
>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const introspectionQuery: string;
|
||||
|
||||
// @public (undocumented)
|
||||
export const Router: () => JSX.Element;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "@backstage/plugin-graphql-voyager",
|
||||
"description": "Backstage plugin for GraphQL Voyager",
|
||||
"version": "0.1.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": "workspace:^",
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/theme": "workspace:^",
|
||||
"@material-ui/core": "^4.9.13",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.57",
|
||||
"graphql-voyager": "^1.0.0-rc.31",
|
||||
"react-use": "^17.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1 || ^17.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/core-app-api": "workspace:^",
|
||||
"@backstage/dev-utils": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^12.1.3",
|
||||
"@testing-library/user-event": "^14.0.0",
|
||||
"@types/node": "*",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"msw": "^0.47.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2023 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 { renderInTestApp } from '@backstage/test-utils';
|
||||
import { GraphQLVoyagerBrowser } from './GraphQLVoyagerBrowser';
|
||||
|
||||
jest.mock('graphql-voyager', () => ({ Voyager: () => '<Voyager />' }));
|
||||
|
||||
describe('GraphQLVoyagerBrowser', () => {
|
||||
it('should render error text if there are no endpoints', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<GraphQLVoyagerBrowser endpoints={[]} />,
|
||||
);
|
||||
|
||||
expect(rendered.getByText('No endpoints available')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render endpoint tabs', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<GraphQLVoyagerBrowser
|
||||
endpoints={[
|
||||
{
|
||||
id: 'a',
|
||||
title: 'Endpoint A',
|
||||
introspectionErrorMessage: 'Failed to introspect A',
|
||||
async introspection() {
|
||||
return {
|
||||
data: {
|
||||
_schema: {},
|
||||
},
|
||||
};
|
||||
},
|
||||
voyagerProps: {
|
||||
hideDocs: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'b',
|
||||
title: 'Endpoint B',
|
||||
introspectionErrorMessage: 'Failed to introspect B',
|
||||
async introspection() {
|
||||
return {
|
||||
data: {
|
||||
_schema: {},
|
||||
},
|
||||
};
|
||||
},
|
||||
voyagerProps: {
|
||||
hideDocs: true,
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(rendered.getByText('Endpoint A')).toBeInTheDocument();
|
||||
expect(rendered.getByText('Endpoint B')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Should render the introspection error message when introspection fails', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<GraphQLVoyagerBrowser
|
||||
endpoints={[
|
||||
{
|
||||
id: 'a',
|
||||
title: 'Endpoint A',
|
||||
introspectionErrorMessage: 'Failed to introspect A',
|
||||
async introspection() {
|
||||
return {};
|
||||
},
|
||||
voyagerProps: {
|
||||
hideDocs: true,
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(rendered.getByText('Failed to introspect A')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2023 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 {
|
||||
GraphQLVoyagerEndpoint,
|
||||
introspectionQuery,
|
||||
} from '../../lib/api/types';
|
||||
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
||||
import { Divider, makeStyles, Tab, Tabs, Typography } from '@material-ui/core';
|
||||
import React, { Suspense } from 'react';
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
import { Content, Progress } from '@backstage/core-components';
|
||||
import { Voyager } from 'graphql-voyager';
|
||||
|
||||
const useStyles = makeStyles<BackstageTheme>(theme => ({
|
||||
root: {
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexFlow: 'column nowrap',
|
||||
},
|
||||
tabs: {
|
||||
background: theme.palette.background.paper,
|
||||
},
|
||||
}));
|
||||
|
||||
type GraphQLVoyagerBrowserProps = {
|
||||
endpoints: GraphQLVoyagerEndpoint[];
|
||||
};
|
||||
|
||||
type IntrospectionResult = {
|
||||
data?: Object;
|
||||
};
|
||||
|
||||
const NoEndpoints = () => {
|
||||
return <Typography variant="h4">No endpoints available</Typography>;
|
||||
};
|
||||
|
||||
const VoyagerBrowser = (props: GraphQLVoyagerBrowserProps) => {
|
||||
const { endpoints } = props;
|
||||
const [tabIndex, setTabIndex] = useState(0);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [introspectionResult, setIntrospectionResult]: [
|
||||
IntrospectionResult,
|
||||
Dispatch<SetStateAction<IntrospectionResult>>,
|
||||
] = useState({});
|
||||
const classes = useStyles();
|
||||
|
||||
const {
|
||||
voyagerProps = {},
|
||||
introspectionErrorMessage,
|
||||
introspection,
|
||||
} = endpoints[tabIndex];
|
||||
|
||||
useEffect(() => {
|
||||
const fetchIntrospection = async () => {
|
||||
setIsLoading(true);
|
||||
|
||||
const data = await introspection(introspectionQuery);
|
||||
setIntrospectionResult(data);
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
fetchIntrospection();
|
||||
}, [tabIndex, introspection]);
|
||||
|
||||
let voyagerContent: JSX.Element;
|
||||
|
||||
if (isLoading) {
|
||||
voyagerContent = <Typography variant="body1">Loading...</Typography>;
|
||||
} else if (!introspectionResult.data) {
|
||||
voyagerContent = (
|
||||
<Typography variant="h4" color="error">
|
||||
{introspectionErrorMessage}
|
||||
</Typography>
|
||||
);
|
||||
} else {
|
||||
voyagerContent = (
|
||||
<Voyager
|
||||
key={tabIndex}
|
||||
introspection={introspectionResult}
|
||||
{...voyagerProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Suspense fallback={<Progress />}>
|
||||
<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>{voyagerContent}</Content>
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export const GraphQLVoyagerBrowser = (props: GraphQLVoyagerBrowserProps) => {
|
||||
const hasEndpoints = checkEndpoints(props);
|
||||
|
||||
if (!hasEndpoints) {
|
||||
return <NoEndpoints />;
|
||||
}
|
||||
return <VoyagerBrowser {...props} />;
|
||||
};
|
||||
|
||||
function checkEndpoints(props: GraphQLVoyagerBrowserProps) {
|
||||
return props.endpoints.length;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2023 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 * from './GraphQLVoyagerBrowser';
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2023 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 { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
|
||||
import { GraphQLVoyagerPage } from './GraphQLVoyagerPage';
|
||||
import { GraphQLVoyagerApi, graphQlVoyagerApiRef } from '../../lib/api';
|
||||
|
||||
jest.mock('../GraphQLVoyagerBrowser', () => ({
|
||||
GraphQLVoyagerBrowser: () => '<GraphQLVoyagerBrowser />',
|
||||
}));
|
||||
|
||||
describe('GraphQLVoyagerPage', () => {
|
||||
it('should show error when no endpoints are loaded', async () => {
|
||||
const loadingApi: GraphQLVoyagerApi = {
|
||||
async getEndpoints() {
|
||||
throw new Error('No endpoints');
|
||||
},
|
||||
};
|
||||
|
||||
const rendered = await renderInTestApp(
|
||||
<TestApiProvider apis={[[graphQlVoyagerApiRef, loadingApi]]}>
|
||||
<GraphQLVoyagerPage />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
expect(rendered.getByText('Welcome to Voyager!')).toBeInTheDocument();
|
||||
expect(
|
||||
rendered.getByText(
|
||||
'Failed to load GraphQL endpoints, Error: No endpoints',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show GraphQLVoyagerBrowser', async () => {
|
||||
const loadingApi: GraphQLVoyagerApi = {
|
||||
async getEndpoints() {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
|
||||
const rendered = await renderInTestApp(
|
||||
<TestApiProvider apis={[[graphQlVoyagerApiRef, loadingApi]]}>
|
||||
<GraphQLVoyagerPage />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
expect(rendered.getByText('Welcome to Voyager!')).toBeInTheDocument();
|
||||
expect(rendered.getByText('<GraphQLVoyagerBrowser />')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2023 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 { Header, Page, Content, Progress } from '@backstage/core-components';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import 'graphql-voyager/dist/voyager.css';
|
||||
import { graphQlVoyagerApiRef } from '../../lib/api';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import { Typography } from '@material-ui/core';
|
||||
import { GraphQLVoyagerBrowser } from '../GraphQLVoyagerBrowser';
|
||||
|
||||
/** @public */
|
||||
export const GraphQLVoyagerPage = () => {
|
||||
const graphQLVoyagerApi = useApi(graphQlVoyagerApiRef);
|
||||
const { value, loading, error } = useAsync(() =>
|
||||
graphQLVoyagerApi.getEndpoints(),
|
||||
);
|
||||
|
||||
let content: JSX.Element;
|
||||
|
||||
if (loading) {
|
||||
content = (
|
||||
<Content>
|
||||
<Progress />
|
||||
</Content>
|
||||
);
|
||||
} else if (error) {
|
||||
content = (
|
||||
<Content>
|
||||
<Typography variant="h4" color="error">
|
||||
Failed to load GraphQL endpoints, {String(error)}
|
||||
</Typography>
|
||||
</Content>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<Content noPadding>
|
||||
<GraphQLVoyagerBrowser endpoints={value!} />
|
||||
</Content>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Page themeId="tool">
|
||||
<Header title="Welcome to Voyager!" />
|
||||
{content}
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2023 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 * from './GraphQLVoyagerPage';
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2023 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 * from './GraphQLVoyagerPage';
|
||||
export * from './GraphQLVoyagerBrowser';
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2023 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 { graphqlVoyagerPlugin, GraphqlVoyagerPage } from './plugin';
|
||||
export { GraphQLVoyagerPage as Router } from './components';
|
||||
export * from './lib/api';
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2023 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 { GraphQLVoyagerApi, GraphQLVoyagerEndpoint } from './types';
|
||||
|
||||
/** @public */
|
||||
export class GraphQLVoyagerEndpoints implements GraphQLVoyagerApi {
|
||||
private constructor(private readonly endpoints: GraphQLVoyagerEndpoint[]) {}
|
||||
|
||||
static from(endpoints: GraphQLVoyagerEndpoint[]) {
|
||||
return new GraphQLVoyagerEndpoints(endpoints);
|
||||
}
|
||||
|
||||
async getEndpoints() {
|
||||
return this.endpoints;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2023 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 * from './GraphQLVoyagerEndpoints';
|
||||
export * from './types';
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2023 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 { createApiRef } from '@backstage/core-plugin-api';
|
||||
import { VoyagerDisplayOptions } from 'graphql-voyager/typings/components/Voyager';
|
||||
import { WorkerCallback } from 'graphql-voyager/typings/utils/types';
|
||||
|
||||
/** @public */
|
||||
export type GraphQLVoyagerEndpoint = {
|
||||
id: string;
|
||||
title: string;
|
||||
introspection: (body: any) => Promise<any>;
|
||||
introspectionErrorMessage: string;
|
||||
voyagerProps?: {
|
||||
displayOptions?: VoyagerDisplayOptions;
|
||||
hideDocs?: boolean;
|
||||
hideSettings?: boolean;
|
||||
workerURI?: string;
|
||||
loadWorker?: WorkerCallback;
|
||||
};
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type GraphQLVoyagerApi = {
|
||||
getEndpoints(): Promise<GraphQLVoyagerEndpoint[]>;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export const graphQlVoyagerApiRef = createApiRef<GraphQLVoyagerApi>({
|
||||
id: 'plugin.graphqlvoyager.api',
|
||||
});
|
||||
|
||||
/** @public */
|
||||
export const introspectionQuery: string = `query IntrospectionQuery {
|
||||
__schema {
|
||||
|
||||
queryType { name }
|
||||
mutationType { name }
|
||||
subscriptionType { name }
|
||||
types {
|
||||
...FullType
|
||||
}
|
||||
directives {
|
||||
name
|
||||
description
|
||||
|
||||
locations
|
||||
args {
|
||||
...InputValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment FullType on __Type {
|
||||
kind
|
||||
name
|
||||
description
|
||||
|
||||
fields(includeDeprecated: true) {
|
||||
name
|
||||
description
|
||||
args {
|
||||
...InputValue
|
||||
}
|
||||
type {
|
||||
...TypeRef
|
||||
}
|
||||
isDeprecated
|
||||
deprecationReason
|
||||
}
|
||||
inputFields {
|
||||
...InputValue
|
||||
}
|
||||
interfaces {
|
||||
...TypeRef
|
||||
}
|
||||
enumValues(includeDeprecated: true) {
|
||||
name
|
||||
description
|
||||
isDeprecated
|
||||
deprecationReason
|
||||
}
|
||||
possibleTypes {
|
||||
...TypeRef
|
||||
}
|
||||
}
|
||||
|
||||
fragment InputValue on __InputValue {
|
||||
name
|
||||
description
|
||||
type { ...TypeRef }
|
||||
defaultValue
|
||||
|
||||
|
||||
}
|
||||
|
||||
fragment TypeRef on __Type {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2023 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 { graphqlVoyagerPlugin } from './plugin';
|
||||
|
||||
describe('graphql-voyager', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(graphqlVoyagerPlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2023 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';
|
||||
|
||||
/** @public */
|
||||
export const graphqlVoyagerPlugin = createPlugin({
|
||||
id: 'graphql-voyager',
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
/** @public */
|
||||
export const GraphqlVoyagerPage = graphqlVoyagerPlugin.provide(
|
||||
createRoutableExtension({
|
||||
name: 'GraphqlVoyagerPage',
|
||||
component: () => import('./components').then(m => m.GraphQLVoyagerPage),
|
||||
mountPoint: rootRouteRef,
|
||||
}),
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2023 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: 'graphql-voyager',
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2023 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';
|
||||
Reference in New Issue
Block a user