diff --git a/.changeset/tasty-jobs-help.md b/.changeset/tasty-jobs-help.md new file mode 100644 index 0000000000..02afa71656 --- /dev/null +++ b/.changeset/tasty-jobs-help.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-graphql-voyager': minor +--- + +Add the new GraphQL Voyager tool as a plugin for backstage, expose necessary API and components needed for it. diff --git a/plugins/graphql-voyager/README.md b/plugins/graphql-voyager/README.md index f2f5265e2f..fe87e1d843 100644 --- a/plugins/graphql-voyager/README.md +++ b/plugins/graphql-voyager/README.md @@ -22,11 +22,11 @@ In order to be able to navigate to the graphQL voyager page, a new route needs t ```tsx // packages/app/App.tsx - import { GraphQLVoyagerPage } from "@backstage/plugin-graphql-voyager"; + import { GraphQLVoyagerPage } from '@backstage/plugin-graphql-voyager'; const routes = ( - }/> + }/> ``` ### Configuration @@ -36,15 +36,15 @@ In order for the plugin to function correctly. GraphQL endpoints need to be adde Add your own configuration to the `packages/app/src/apis.ts` file the following way: ```ts -import { identityApiRef } from './IdentityApi'; -import { GraphQLVoyagerEndpoints } from './GraphQLVoyagerEndpoints'; +import { identityApiRef } from '@backstage/core-plugin-api'; +import { GraphQLVoyagerEndpoints } from '@backstage/plugin-graphql-voyager'; export const apis: AnyApiFactory[] = [ createApiFactory({ api: graphQlVoyagerApiRef, deps: { identityApi: identityApiRef }, factory: ({ identityApiRef }) => { - GraphQLVoyagerEndpoints.from([ + return GraphQLVoyagerEndpoints.from([ { id: `graphql-voyager-endpoint-id`, title: 'endpoint-title', diff --git a/plugins/graphql-voyager/package.json b/plugins/graphql-voyager/package.json index 1cc7bb19e6..02e8ec95ca 100644 --- a/plugins/graphql-voyager/package.json +++ b/plugins/graphql-voyager/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphql-voyager", "description": "Backstage plugin for GraphQL Voyager", - "version": "0.1.0", + "version": "0.0.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -26,7 +26,7 @@ "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/theme": "workspace:^", - "@material-ui/core": "^4.9.13", + "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", "graphql-voyager": "^1.0.0-rc.31", @@ -45,7 +45,7 @@ "@testing-library/user-event": "^14.0.0", "@types/node": "*", "cross-fetch": "^3.1.5", - "msw": "^0.47.0" + "msw": "^0.49.0" }, "files": [ "dist" diff --git a/plugins/graphql-voyager/src/components/GraphQLVoyagerBrowser/GraphQLVoyagerBrowser.tsx b/plugins/graphql-voyager/src/components/GraphQLVoyagerBrowser/GraphQLVoyagerBrowser.tsx index a4a5533642..d2f9262983 100644 --- a/plugins/graphql-voyager/src/components/GraphQLVoyagerBrowser/GraphQLVoyagerBrowser.tsx +++ b/plugins/graphql-voyager/src/components/GraphQLVoyagerBrowser/GraphQLVoyagerBrowser.tsx @@ -17,100 +17,76 @@ 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 { useState } from 'react'; +import { 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 { Content, ErrorPanel, Progress } from '@backstage/core-components'; import { Voyager } from 'graphql-voyager'; +import useAsync from 'react-use/lib/useAsync'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles(() => ({ 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 VoyagerBrowserContent = ({ + endpoint, +}: { + endpoint: GraphQLVoyagerEndpoint; +}) => { + const { id, introspection, introspectionErrorMessage, voyagerProps } = + endpoint; + const { value, loading, error } = useAsync( + () => introspection(introspectionQuery), + [endpoint], + ); + + if (loading) { + return ; + } else if (error) { + return ; + } + return value.data ? ( + + ) : ( + + {introspectionErrorMessage} + + ); +}; + 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) => ( - + {endpoints.map(({ title, id }, index) => ( + ))} - - {voyagerContent} + + +
); diff --git a/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/GraphQLVoyagerPage.test.tsx b/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/GraphQLVoyagerPage.test.tsx index e40e81d7a6..67c257be2b 100644 --- a/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/GraphQLVoyagerPage.test.tsx +++ b/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/GraphQLVoyagerPage.test.tsx @@ -38,11 +38,7 @@ describe('GraphQLVoyagerPage', () => { ); expect(rendered.getByText('Welcome to Voyager!')).toBeInTheDocument(); - expect( - rendered.getByText( - 'Failed to load GraphQL endpoints, Error: No endpoints', - ), - ).toBeInTheDocument(); + expect(rendered.getByText('Error: No endpoints')).toBeInTheDocument(); }); it('should show GraphQLVoyagerBrowser', async () => { diff --git a/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/GraphQLVoyagerPage.tsx b/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/GraphQLVoyagerPage.tsx index fa15eae86f..8b32a25f87 100644 --- a/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/GraphQLVoyagerPage.tsx +++ b/plugins/graphql-voyager/src/components/GraphQLVoyagerPage/GraphQLVoyagerPage.tsx @@ -14,49 +14,48 @@ * limitations under the License. */ import React from 'react'; -import { Header, Page, Content, Progress } from '@backstage/core-components'; +import { + Header, + Page, + Content, + Progress, + ErrorPanel, +} 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 = () => { +type GraphQLVoyagerPageProps = { + title?: string; +}; + +const VoyagerContent = () => { const graphQLVoyagerApi = useApi(graphQlVoyagerApiRef); + const { value, loading, error } = useAsync(() => graphQLVoyagerApi.getEndpoints(), ); - let content: JSX.Element; - if (loading) { - content = ( - - - - ); + return ; } else if (error) { - content = ( - - - Failed to load GraphQL endpoints, {String(error)} - - - ); - } else { - content = ( - - - - ); + return ; } + return ; +}; + +/** @public */ +export const GraphQLVoyagerPage = (props: GraphQLVoyagerPageProps) => { + const { title = 'Welcome to Voyager!' } = props; return ( -
- {content} +
+ + + ); };