Skip to content

Commit

Permalink
Switch to accepting schema as introspection
Browse files Browse the repository at this point in the history
Also fixed number of small bugs and removed custom introspection format.
  • Loading branch information
IvanGoncharov committed Aug 3, 2023
1 parent fbc5abb commit 064a625
Show file tree
Hide file tree
Showing 34 changed files with 1,212 additions and 939 deletions.
6 changes: 6 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ overrides:
'@typescript-eslint/array-type': [error, { default: generic }]
'@typescript-eslint/consistent-indexed-object-style':
[error, index-signature]
'@typescript-eslint/no-unused-vars':
- 'error'
- vars: all
args: all
argsIgnorePattern: '^_'
varsIgnorePattern: '^_T'

# FIXME: remove below rules
'react/jsx-key': off
Expand Down
20 changes: 4 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,12 @@
"@mui/lab": "5.0.0-alpha.114",
"@mui/material": "5.11.2",
"commonmark": "0.30.0",
"lodash": "4.17.21",
"svg-pan-zoom": "3.6.1"
},
"devDependencies": {
"@playwright/test": "1.32.0",
"@svgr/webpack": "6.5.1",
"@types/commonmark": "0.27.5",
"@types/lodash": "4.14.182",
"@types/node": "18.15.5",
"@types/react": "18.0.26",
"@types/react-dom": "18.0.10",
Expand Down
2 changes: 1 addition & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const config: PlaywrightTestConfig = {
actionTimeout: 0,

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
trace: 'retain-on-failure',
},
projects: [
{
Expand Down
29 changes: 23 additions & 6 deletions src/components/GraphViewport.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Component } from 'react';

import { SVGRender, Viewport } from './../graph/';
import { SVGRender, TypeGraph, Viewport } from './../graph/';
import LoadingAnimation from './utils/LoadingAnimation';
import { VoyagerDisplayOptions } from './Voyager';

const svgRenderer = new SVGRender();

interface GraphViewportProps {
typeGraph: any;
displayOptions: any;
typeGraph: TypeGraph | null;
displayOptions: VoyagerDisplayOptions;

selectedTypeID: string;
selectedEdgeID: string;
Expand All @@ -16,8 +17,21 @@ interface GraphViewportProps {
onSelectEdge: (id: string) => void;
}

export default class GraphViewport extends Component<GraphViewportProps> {
state = { typeGraph: null, displayOptions: null, svgViewport: null };
interface GraphViewportState {
typeGraph: TypeGraph | null;
displayOptions: VoyagerDisplayOptions | null;
svgViewport: Viewport | null;
}

export default class GraphViewport extends Component<
GraphViewportProps,
GraphViewportState
> {
state: GraphViewportState = {
typeGraph: null,
displayOptions: null,
svgViewport: null,
};

// Handle async graph rendering based on this example
// https://gist.github.com/bvaughn/982ab689a41097237f6e9860db7ca8d6
Expand Down Expand Up @@ -69,7 +83,10 @@ export default class GraphViewport extends Component<GraphViewportProps> {
this._cleanupSvgViewport();
}

_renderSvgAsync(typeGraph, displayOptions) {
_renderSvgAsync(
typeGraph: TypeGraph | null,
displayOptions: VoyagerDisplayOptions | null,
) {
if (typeGraph == null || displayOptions == null) {
return; // Nothing to render
}
Expand Down
9 changes: 5 additions & 4 deletions src/components/IntrospectionModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import TextField from '@mui/material/TextField';
import Tooltip from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';
import Grid from '@mui/material/Unstable_Grid2';
import { buildClientSchema } from 'graphql/utilities';
import { useState } from 'react';

import { voyagerIntrospectionQuery } from '../utils/introspection-query';
import { sdlToIntrospection } from '../utils/sdl-to-introspection';
import { sdlToSchema } from '../utils/sdl-to-introspection';

enum InputType {
Presets = 'Presets',
Expand Down Expand Up @@ -102,14 +103,14 @@ export function IntrospectionModal(props: IntrospectionModalProps) {
function handleSubmit() {
switch (inputType) {
case InputType.Presets:
onChange(presets[activePreset].data);
onChange(buildClientSchema(presets[activePreset].data));
break;
case InputType.Introspection:
// check for errors and check if valid
onChange(JSON.parse(jsonText).data);
onChange(buildClientSchema(JSON.parse(jsonText).data));
break;
case InputType.SDL:
onChange(sdlToIntrospection(sdlText));
onChange(sdlToSchema(sdlText));
break;
}
setSubmitted({ inputType, sdlText, jsonText, activePreset });
Expand Down
92 changes: 51 additions & 41 deletions src/components/Voyager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import './viewport.css';
import Button from '@mui/material/Button';
import Stack from '@mui/material/Stack';
import { ThemeProvider } from '@mui/material/styles';
import { ExecutionResult } from 'graphql/execution';
import { GraphQLSchema } from 'graphql/type';
import { buildClientSchema, IntrospectionQuery } from 'graphql/utilities';
import {
Children,
type ReactElement,
Expand All @@ -15,7 +18,7 @@ import {
} from 'react';

import { getTypeGraph } from '../graph/';
import { extractTypeId, getSchema } from '../introspection';
import { extractTypeName, getSchema, typeNameToId } from '../introspection';
import DocExplorer from './doc-explorer/DocExplorer';
import GraphViewport from './GraphViewport';
import { IntrospectionModal } from './IntrospectionModal';
Expand All @@ -33,17 +36,8 @@ export interface VoyagerDisplayOptions {
hideRoot?: boolean;
}

const defaultDisplayOptions = {
rootType: undefined,
skipRelay: true,
skipDeprecated: true,
sortByAlphabet: false,
showLeafFields: true,
hideRoot: false,
};

export interface VoyagerProps {
introspection: unknown;
introspection: Promise<ExecutionResult<IntrospectionQuery> | GraphQLSchema>;
displayOptions?: VoyagerDisplayOptions;
introspectionPresets?: { [name: string]: any };
allowToChangeSchema?: boolean;
Expand All @@ -55,43 +49,59 @@ export interface VoyagerProps {
}

export default function Voyager(props: VoyagerProps) {
const initialDisplayOptions: VoyagerDisplayOptions = useMemo(
() => ({
rootType: undefined,
skipRelay: true,
skipDeprecated: true,
sortByAlphabet: false,
showLeafFields: true,
hideRoot: false,
...props.displayOptions,
}),
[props.displayOptions],
);

const [introspectionModalOpen, setIntrospectionModalOpen] = useState(false);
const [introspectionData, setIntrospectionData] = useState(null);
const [displayOptions, setDisplayOptions] = useState({
...defaultDisplayOptions,
...props.displayOptions,
});
const [introspectionResult, setIntrospectionResult] = useState(null);
const [displayOptions, setDisplayOptions] = useState(initialDisplayOptions);

useEffect(() => {
// FIXME: handle rejection and also handle errors inside introspection
// eslint-disable-next-line @typescript-eslint/no-floating-promises
Promise.resolve(props.introspection).then(({ data }) => {
setIntrospectionData(data);
setSelected({ typeID: null, edgeID: null });
});
Promise.resolve(props.introspection).then(setIntrospectionResult);
}, [props.introspection]);

const schema = useMemo(
() =>
getSchema(
introspectionData,
displayOptions.sortByAlphabet,
displayOptions.skipRelay,
displayOptions.skipDeprecated,
),
[
introspectionData,
useEffect(() => {
setDisplayOptions(initialDisplayOptions);
}, [introspectionResult, initialDisplayOptions]);

const typeGraph = useMemo(() => {
if (introspectionResult == null) {
return null;
}

const introspectionSchema =
introspectionResult instanceof GraphQLSchema
? introspectionResult
: buildClientSchema(introspectionResult.data);

const schema = getSchema(
introspectionSchema,
displayOptions.sortByAlphabet,
displayOptions.skipRelay,
displayOptions.skipDeprecated,
],
);
);
return getTypeGraph(
schema,
displayOptions.rootType,
displayOptions.hideRoot,
);
}, [introspectionResult, displayOptions]);

const typeGraph = useMemo(
() =>
getTypeGraph(schema, displayOptions.rootType, displayOptions.hideRoot),
[schema, displayOptions.rootType, displayOptions.hideRoot],
);
useEffect(() => {
setSelected({ typeID: null, edgeID: null });
}, [typeGraph]);

const [selected, setSelected] = useState({ typeID: null, edgeID: null });

Expand Down Expand Up @@ -123,7 +133,7 @@ export default function Voyager(props: VoyagerProps) {
open={introspectionModalOpen}
presets={props.introspectionPresets}
onClose={() => setIntrospectionModalOpen(false)}
onChange={setIntrospectionData}
onChange={setIntrospectionResult}
/>
);
}
Expand Down Expand Up @@ -171,12 +181,12 @@ export default function Voyager(props: VoyagerProps) {
}

function renderSettings() {
if (schema == null) return null;
if (typeGraph == null) return null;

return (
<Settings
schema={schema}
options={displayOptions}
typeGraph={typeGraph}
onChange={(options) =>
setDisplayOptions((oldOptions) => ({ ...oldOptions, ...options }))
}
Expand Down Expand Up @@ -213,7 +223,7 @@ export default function Voyager(props: VoyagerProps) {
// deselect if click again
return { ...oldSelected, edgeID: null };
} else {
return { typeID: extractTypeId(edgeID), edgeID };
return { typeID: typeNameToId(extractTypeName(edgeID)), edgeID };
}
});
}
Expand Down
12 changes: 8 additions & 4 deletions src/components/doc-explorer/Argument.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
import './Argument.css';

import { GraphQLArgument, GraphQLNamedType } from 'graphql/type';

import { highlightTerm } from '../../utils/highlight';
import Markdown from '../utils/Markdown';
import WrappedTypeName from './WrappedTypeName';

interface ArgumentProps {
arg: any;
arg: GraphQLArgument;
filter: string;
expanded: boolean;
onTypeLink: (any) => void;
onTypeLink: (type: GraphQLNamedType) => void;
}

export default function Argument(props: ArgumentProps) {
const { arg, expanded, onTypeLink } = props;
const { arg, filter, expanded, onTypeLink } = props;

return (
<span className={`arg-wrap ${expanded ? '-expanded' : ''}`}>
<Markdown text={arg.description} className="arg-description" />
<span className="arg">
<span className="arg-name">{arg.name}</span>
<span className="arg-name">{highlightTerm(arg.name, filter)}</span>
<WrappedTypeName container={arg} onTypeLink={onTypeLink} />
{arg.defaultValue != null && (
<span>
Expand Down
Loading

0 comments on commit 064a625

Please sign in to comment.