From 3b5990bca19163c1bd781e312ff1359b9f6ee7bc Mon Sep 17 00:00:00 2001 From: MitchWijt Date: Fri, 27 Jan 2023 16:25:35 +0100 Subject: [PATCH] feat(plugin): Add graphql-voyager as new backstage plugin Signed-off-by: MitchWijt --- microsite/data/plugins/graphql-voyager.yaml | 13 ++ plugins/graphql-voyager/.eslintrc.js | 1 + plugins/graphql-voyager/README.md | 75 ++++++++++ plugins/graphql-voyager/api-report.md | 64 ++++++++ plugins/graphql-voyager/package.json | 53 +++++++ .../GraphQLVoyagerBrowser.test.tsx | 94 ++++++++++++ .../GraphQLVoyagerBrowser.tsx | 131 ++++++++++++++++ .../components/GraphQLVoyagerBrowser/index.ts | 16 ++ .../GraphQLVoyagerPage.test.tsx | 64 ++++++++ .../GraphQLVoyagerPage/GraphQLVoyagerPage.tsx | 62 ++++++++ .../components/GraphQLVoyagerPage/index.ts | 16 ++ .../graphql-voyager/src/components/index.ts | 17 +++ plugins/graphql-voyager/src/index.ts | 18 +++ .../src/lib/api/GraphQLVoyagerEndpoints.ts | 29 ++++ plugins/graphql-voyager/src/lib/api/index.ts | 17 +++ plugins/graphql-voyager/src/lib/api/types.ts | 141 ++++++++++++++++++ plugins/graphql-voyager/src/plugin.test.ts | 22 +++ plugins/graphql-voyager/src/plugin.ts | 38 +++++ plugins/graphql-voyager/src/routes.ts | 20 +++ plugins/graphql-voyager/src/setupTests.ts | 17 +++ 20 files changed, 908 insertions(+) create mode 100644 microsite/data/plugins/graphql-voyager.yaml create mode 100644 plugins/graphql-voyager/.eslintrc.js create mode 100644 plugins/graphql-voyager/README.md create mode 100644 plugins/graphql-voyager/api-report.md create mode 100644 plugins/graphql-voyager/package.json create mode 100644 plugins/graphql-voyager/src/components/GraphQLVoyagerBrowser/GraphQLVoyagerBrowser.test.tsx create mode 100644 plugins/graphql-voyager/src/components/GraphQLVoyagerBrowser/GraphQLVoyagerBrowser.tsx create mode 100644 plugins/graphql-voyager/src/components/GraphQLVoyagerBrowser/index.ts create mode 100644 plugins/graphql-voyager/src/components/GraphQLVoyagerPage/GraphQLVoyagerPage.test.tsx create mode 100644 plugins/graphql-voyager/src/components/GraphQLVoyagerPage/GraphQLVoyagerPage.tsx create mode 100644 plugins/graphql-voyager/src/components/GraphQLVoyagerPage/index.ts create mode 100644 plugins/graphql-voyager/src/components/index.ts create mode 100644 plugins/graphql-voyager/src/index.ts create mode 100644 plugins/graphql-voyager/src/lib/api/GraphQLVoyagerEndpoints.ts create mode 100644 plugins/graphql-voyager/src/lib/api/index.ts create mode 100644 plugins/graphql-voyager/src/lib/api/types.ts create mode 100644 plugins/graphql-voyager/src/plugin.test.ts create mode 100644 plugins/graphql-voyager/src/plugin.ts create mode 100644 plugins/graphql-voyager/src/routes.ts create mode 100644 plugins/graphql-voyager/src/setupTests.ts diff --git a/microsite/data/plugins/graphql-voyager.yaml b/microsite/data/plugins/graphql-voyager.yaml new file mode 100644 index 0000000000..0bb56fb469 --- /dev/null +++ b/microsite/data/plugins/graphql-voyager.yaml @@ -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' diff --git a/plugins/graphql-voyager/.eslintrc.js b/plugins/graphql-voyager/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/graphql-voyager/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/graphql-voyager/README.md b/plugins/graphql-voyager/README.md new file mode 100644 index 0000000000..f2f5265e2f --- /dev/null +++ b/plugins/graphql-voyager/README.md @@ -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 = ( + + }/> +``` + +### 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, + }, + }, + ]); + }, + }), +]; +``` diff --git a/plugins/graphql-voyager/api-report.md b/plugins/graphql-voyager/api-report.md new file mode 100644 index 0000000000..21423a8c30 --- /dev/null +++ b/plugins/graphql-voyager/api-report.md @@ -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 +/// + +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; +}; + +// @public (undocumented) +export const graphQlVoyagerApiRef: ApiRef; + +// @public (undocumented) +export type GraphQLVoyagerEndpoint = { + id: string; + title: string; + introspection: (body: any) => Promise; + 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; +} + +// @public (undocumented) +export const GraphqlVoyagerPage: () => JSX.Element; + +// @public (undocumented) +export const graphqlVoyagerPlugin: BackstagePlugin< + { + root: RouteRef; + }, + {}, + {} +>; + +// @public (undocumented) +export const introspectionQuery: string; + +// @public (undocumented) +export const Router: () => JSX.Element; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/graphql-voyager/package.json b/plugins/graphql-voyager/package.json new file mode 100644 index 0000000000..1cc7bb19e6 --- /dev/null +++ b/plugins/graphql-voyager/package.json @@ -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" + ] +} diff --git a/plugins/graphql-voyager/src/components/GraphQLVoyagerBrowser/GraphQLVoyagerBrowser.test.tsx b/plugins/graphql-voyager/src/components/GraphQLVoyagerBrowser/GraphQLVoyagerBrowser.test.tsx new file mode 100644 index 0000000000..76ccbd9965 --- /dev/null +++ b/plugins/graphql-voyager/src/components/GraphQLVoyagerBrowser/GraphQLVoyagerBrowser.test.tsx @@ -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: () => '' })); + +describe('GraphQLVoyagerBrowser', () => { + it('should render error text if there are no endpoints', async () => { + const rendered = await renderInTestApp( + , + ); + + expect(rendered.getByText('No endpoints available')).toBeInTheDocument(); + }); + + it('should render endpoint tabs', async () => { + const rendered = await renderInTestApp( + , + ); + + 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( + , + ); + + expect(rendered.getByText('Failed to introspect A')).toBeInTheDocument(); + }); +}); diff --git a/plugins/graphql-voyager/src/components/GraphQLVoyagerBrowser/GraphQLVoyagerBrowser.tsx b/plugins/graphql-voyager/src/components/GraphQLVoyagerBrowser/GraphQLVoyagerBrowser.tsx new file mode 100644 index 0000000000..a4a5533642 --- /dev/null +++ b/plugins/graphql-voyager/src/components/GraphQLVoyagerBrowser/GraphQLVoyagerBrowser.tsx @@ -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(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 No endpoints available; +}; + +const VoyagerBrowser = (props: GraphQLVoyagerBrowserProps) => { + const { endpoints } = props; + const [tabIndex, setTabIndex] = useState(0); + const [isLoading, setIsLoading] = useState(true); + const [introspectionResult, setIntrospectionResult]: [ + IntrospectionResult, + Dispatch>, + ] = 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 = Loading...; + } else if (!introspectionResult.data) { + voyagerContent = ( + + {introspectionErrorMessage} + + ); + } else { + voyagerContent = ( + + ); + } + + return ( +
+ }> + setTabIndex(value)} + indicatorColor="primary" + > + {endpoints.map(({ title }, index) => ( + + ))} + + + {voyagerContent} + +
+ ); +}; + +/** @public */ +export const GraphQLVoyagerBrowser = (props: GraphQLVoyagerBrowserProps) => { + const hasEndpoints = checkEndpoints(props); + + if (!hasEndpoints) { + return ; + } + return ; +}; + +function checkEndpoints(props: GraphQLVoyagerBrowserProps) { + return props.endpoints.length; +} diff --git a/plugins/graphql-voyager/src/components/GraphQLVoyagerBrowser/index.ts b/plugins/graphql-voyager/src/components/GraphQLVoyagerBrowser/index.ts new file mode 100644 index 0000000000..3beb0ccd95 --- /dev/null +++ b/plugins/graphql-voyager/src/components/GraphQLVoyagerBrowser/index.ts @@ -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'; diff --git a/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/GraphQLVoyagerPage.test.tsx b/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/GraphQLVoyagerPage.test.tsx new file mode 100644 index 0000000000..e40e81d7a6 --- /dev/null +++ b/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/GraphQLVoyagerPage.test.tsx @@ -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: () => '', +})); + +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( + + + , + ); + + 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( + + + , + ); + + expect(rendered.getByText('Welcome to Voyager!')).toBeInTheDocument(); + expect(rendered.getByText('')).toBeInTheDocument(); + }); +}); diff --git a/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/GraphQLVoyagerPage.tsx b/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/GraphQLVoyagerPage.tsx new file mode 100644 index 0000000000..fa15eae86f --- /dev/null +++ b/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/GraphQLVoyagerPage.tsx @@ -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 = ( + + + + ); + } else if (error) { + content = ( + + + Failed to load GraphQL endpoints, {String(error)} + + + ); + } else { + content = ( + + + + ); + } + + return ( + +
+ {content} + + ); +}; diff --git a/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/index.ts b/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/index.ts new file mode 100644 index 0000000000..5ee042fd5f --- /dev/null +++ b/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/index.ts @@ -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'; diff --git a/plugins/graphql-voyager/src/components/index.ts b/plugins/graphql-voyager/src/components/index.ts new file mode 100644 index 0000000000..76c4229839 --- /dev/null +++ b/plugins/graphql-voyager/src/components/index.ts @@ -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'; diff --git a/plugins/graphql-voyager/src/index.ts b/plugins/graphql-voyager/src/index.ts new file mode 100644 index 0000000000..2e7852bc51 --- /dev/null +++ b/plugins/graphql-voyager/src/index.ts @@ -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'; diff --git a/plugins/graphql-voyager/src/lib/api/GraphQLVoyagerEndpoints.ts b/plugins/graphql-voyager/src/lib/api/GraphQLVoyagerEndpoints.ts new file mode 100644 index 0000000000..4d08ffbce3 --- /dev/null +++ b/plugins/graphql-voyager/src/lib/api/GraphQLVoyagerEndpoints.ts @@ -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; + } +} diff --git a/plugins/graphql-voyager/src/lib/api/index.ts b/plugins/graphql-voyager/src/lib/api/index.ts new file mode 100644 index 0000000000..c52eedafb7 --- /dev/null +++ b/plugins/graphql-voyager/src/lib/api/index.ts @@ -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'; diff --git a/plugins/graphql-voyager/src/lib/api/types.ts b/plugins/graphql-voyager/src/lib/api/types.ts new file mode 100644 index 0000000000..08e6f13923 --- /dev/null +++ b/plugins/graphql-voyager/src/lib/api/types.ts @@ -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; + introspectionErrorMessage: string; + voyagerProps?: { + displayOptions?: VoyagerDisplayOptions; + hideDocs?: boolean; + hideSettings?: boolean; + workerURI?: string; + loadWorker?: WorkerCallback; + }; +}; + +/** @public */ +export type GraphQLVoyagerApi = { + getEndpoints(): Promise; +}; + +/** @public */ +export const graphQlVoyagerApiRef = createApiRef({ + 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 + } + } + } + } + } + } + } + }`; diff --git a/plugins/graphql-voyager/src/plugin.test.ts b/plugins/graphql-voyager/src/plugin.test.ts new file mode 100644 index 0000000000..df7acc7143 --- /dev/null +++ b/plugins/graphql-voyager/src/plugin.test.ts @@ -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(); + }); +}); diff --git a/plugins/graphql-voyager/src/plugin.ts b/plugins/graphql-voyager/src/plugin.ts new file mode 100644 index 0000000000..bd33209a1d --- /dev/null +++ b/plugins/graphql-voyager/src/plugin.ts @@ -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, + }), +); diff --git a/plugins/graphql-voyager/src/routes.ts b/plugins/graphql-voyager/src/routes.ts new file mode 100644 index 0000000000..12f06c1cb5 --- /dev/null +++ b/plugins/graphql-voyager/src/routes.ts @@ -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', +}); diff --git a/plugins/graphql-voyager/src/setupTests.ts b/plugins/graphql-voyager/src/setupTests.ts new file mode 100644 index 0000000000..73dd8dce47 --- /dev/null +++ b/plugins/graphql-voyager/src/setupTests.ts @@ -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';