Skip to content

Commit

Permalink
fix: improve setSchema & schema loading, allow primitive schema (grap…
Browse files Browse the repository at this point in the history
…hql#1648)

- allow null schema
- `api.setSchema()` works on it's own, and static string is evaluated as a schema
- `api.setSchema()` now allows `string | GraphQLSchema`

Co-authored-by: Rikki <rikki.schulte@gmail.com>
  • Loading branch information
2 people authored and thenamankumar committed Aug 30, 2020
1 parent 75562de commit c8008f7
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 65 deletions.
120 changes: 113 additions & 7 deletions examples/monaco-graphql-webpack/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,74 @@ window.MonacoEnvironment = {
},
};

const schemas = {
remote: {
op: `
query Example($limit: Int) {
launchesPast(limit: $limit) {
mission_name
# format me using the right click context menu
launch_date_local
launch_site {
site_name_long
}
links {
article_link
video_link
}
}
}
`,
variables: `{ "limit": 10 }`,
load: ({ op, variables }: { op: string; variables: string }) => {
GraphQLAPI.setSchemaConfig({ uri: SCHEMA_URL });
variablesEditor.setValue(variables);
operationEditor.setValue(op);
},
},
local: {
op: `query Example {
allTodos {
id
name
}
}`,
load: ({ op }: { op: string }) => {
setRawSchema();
variablesEditor.setValue('{}');
operationEditor.setValue(op);
},
},
};

const THEME = 'vs-dark';

const schemaInput = document.createElement('input');
schemaInput.type = 'text';

schemaInput.value = SCHEMA_URL;

const selectEl = document.createElement('select');

selectEl.onchange = e => {
e.preventDefault();
const type = selectEl.value as 'local' | 'remote';
if (schemas[type]) {
// @ts-ignore
schemas[type].load(schemas[type]);
}
};

const createOption = (label: string, value: string) => {
const el = document.createElement('option');
el.label = label;
el.value = value;
return el;
};

selectEl.appendChild(createOption('Remote', 'remote'));
selectEl.appendChild(createOption('Local', 'local'));

schemaInput.onkeyup = e => {
e.preventDefault();
// @ts-ignore
Expand All @@ -44,6 +107,41 @@ schemaInput.onkeyup = e => {

const toolbar = document.getElementById('toolbar');
toolbar?.appendChild(schemaInput);
toolbar?.appendChild(selectEl);

async function setRawSchema() {
await GraphQLAPI.setSchema(`# Enumeration type for a level of priority
enum Priority {
LOW
MEDIUM
HIGH
}
# Our main todo type
type Todo {
id: ID!
name: String!
description: String
priority: Priority!
}
type Query {
# Get one todo item
todo(id: ID!): Todo
# Get all todo items
allTodos: [Todo!]!
}
type Mutation {
addTodo(name: String!, priority: Priority = LOW): Todo!
removeTodo(id: ID!): Todo!
}
schema {
query: Query
mutation: Mutation
}`);
}

const variablesModel = monaco.editor.createModel(
`{}`,
Expand All @@ -56,6 +154,7 @@ const resultsEditor = monaco.editor.create(
{
model: variablesModel,
automaticLayout: true,
theme: THEME,
},
);
const variablesEditor = monaco.editor.create(
Expand All @@ -64,11 +163,12 @@ const variablesEditor = monaco.editor.create(
value: `{ "limit": 10 }`,
language: 'json',
automaticLayout: true,
theme: THEME,
},
);
const model = monaco.editor.createModel(
`
query Example($limit: Int) {
query Example($limit: Int) {
launchesPast(limit: $limit) {
mission_name
# format me using the right click context menu
Expand All @@ -95,6 +195,7 @@ const operationEditor = monaco.editor.create(
formatOnPaste: true,
formatOnType: true,
folding: true,
theme: THEME,
},
);

Expand All @@ -104,8 +205,6 @@ GraphQLAPI.setFormattingOptions({
},
});

GraphQLAPI.setSchemaConfig({ uri: SCHEMA_URL });

/**
* Basic Operation Exec Example
*/
Expand Down Expand Up @@ -148,6 +247,17 @@ operationEditor.addAction(opAction);
variablesEditor.addAction(opAction);
resultsEditor.addAction(opAction);

/**
* load local schema by default
*/

let initialSchema = false;

if (!initialSchema) {
schemas.remote.load(schemas.remote);
initialSchema = true;
}

// add your own diagnostics? why not!
// monaco.editor.setModelMarkers(
// model,
Expand All @@ -161,7 +271,3 @@ resultsEditor.addAction(opAction);
// endColumn: 0,
// }],
// );

// operationEditor.onDidChangeModelContent(() => {
// // this is where
// })
71 changes: 42 additions & 29 deletions packages/graphql-language-service/src/LanguageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import {
getHoverInformation,
} from 'graphql-language-service-interface';

import { RawSchema } from './types';

import {
defaultSchemaLoader,
SchemaConfig,
Expand All @@ -25,7 +23,7 @@ export type GraphQLLanguageConfig = {
parser?: typeof parse;
schemaLoader?: typeof defaultSchemaLoader;
schemaBuilder?: typeof defaultSchemaBuilder;
rawSchema?: RawSchema;
schemaString?: string;
parseOptions?: ParseOptions;
schemaConfig: SchemaConfig;
};
Expand All @@ -37,16 +35,16 @@ export class LanguageService {
private _schemaResponse: SchemaResponse | null = null;
private _schemaLoader: (
schemaConfig: SchemaConfig,
) => Promise<SchemaResponse | void> = defaultSchemaLoader;
) => Promise<SchemaResponse | null> = defaultSchemaLoader;
private _schemaBuilder = defaultSchemaBuilder;
private _rawSchema: RawSchema | null = null;
private _schemaString: string | null = null;
private _parseOptions: ParseOptions | undefined = undefined;
constructor({
parser,
schemaLoader,
schemaBuilder,
schemaConfig,
rawSchema,
schemaString,
parseOptions,
}: GraphQLLanguageConfig) {
this._schemaConfig = schemaConfig;
Expand All @@ -59,8 +57,8 @@ export class LanguageService {
if (schemaBuilder) {
this._schemaBuilder = schemaBuilder;
}
if (rawSchema) {
this._rawSchema = rawSchema;
if (schemaString) {
this._schemaString = schemaString;
}
if (parseOptions) {
this._parseOptions = parseOptions;
Expand All @@ -80,28 +78,28 @@ export class LanguageService {

/**
* setSchema statically, ignoring URI
* @param schema {RawSchema}
* @param schema {schemaString}
*/
public async setSchema(schema: RawSchema): Promise<GraphQLSchema> {
this._rawSchema = schema;
return this.loadSchema();
public async setSchema(schema: string): Promise<void> {
this._schemaString = schema;
await this.loadSchema();
}

public async getSchemaResponse(): Promise<SchemaResponse> {
public async getSchemaResponse(): Promise<SchemaResponse | null> {
if (this._schemaResponse) {
return this._schemaResponse;
}
return this.loadSchemaResponse();
}

public async loadSchemaResponse(): Promise<SchemaResponse> {
if (this._rawSchema) {
return typeof this._rawSchema === 'string'
? this.parse(this._rawSchema)
: this._rawSchema;
public async loadSchemaResponse(): Promise<SchemaResponse | null> {
if (this._schemaString) {
return typeof this._schemaString === 'string'
? this.parse(this._schemaString)
: this._schemaString;
}
if (!this._schemaConfig?.uri) {
throw new Error('uri missing');
return null;
}
this._schemaResponse = (await this._schemaLoader(
this._schemaConfig,
Expand All @@ -111,11 +109,15 @@ export class LanguageService {

public async loadSchema() {
const schemaResponse = await this.loadSchemaResponse();
this._schema = this._schemaBuilder(
schemaResponse,
this._schemaConfig.buildSchemaOptions,
) as GraphQLSchema;
return this._schema;
if (schemaResponse) {
this._schema = this._schemaBuilder(
schemaResponse,
this._schemaConfig.buildSchemaOptions,
) as GraphQLSchema;
return this._schema;
} else {
return null;
}
}

public async parse(text: string, options?: ParseOptions) {
Expand All @@ -126,23 +128,34 @@ export class LanguageService {
_uri: string,
documentText: string,
position: Position,
) =>
getAutocompleteSuggestions(await this.getSchema(), documentText, position);
) => {
const schema = await this.getSchema();
if (!schema) {
return [];
}
return getAutocompleteSuggestions(schema, documentText, position);
};

public getDiagnostics = async (
_uri: string,
documentText: string,
customRules?: ValidationRule[],
) => {
if (!documentText || documentText.length < 1) {
const schema = await this.getSchema();
if (!documentText || documentText.length < 1 || !schema) {
return [];
}
return getDiagnostics(documentText, await this.getSchema(), customRules);
return getDiagnostics(documentText, schema, customRules);
};

public getHover = async (
_uri: string,
documentText: string,
position: Position,
) => getHoverInformation(await this.getSchema(), documentText, position);
) =>
getHoverInformation(
(await this.getSchema()) as GraphQLSchema,
documentText,
position,
);
}
11 changes: 8 additions & 3 deletions packages/graphql-language-service/src/schemaLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,25 @@ import {
} from 'graphql';

export type SchemaConfig = {
uri: string;
uri?: string;
requestOpts?: RequestInit;
introspectionOptions?: IntrospectionOptions;
buildSchemaOptions?: BuildSchemaOptions;
};

export type SchemaResponse = IntrospectionQuery | DocumentNode;

export type SchemaLoader = (config: SchemaConfig) => Promise<SchemaResponse>;
export type SchemaLoader = (
config: SchemaConfig,
) => Promise<SchemaResponse | null>;

export const defaultSchemaLoader: SchemaLoader = async (
schemaConfig: SchemaConfig,
): Promise<SchemaResponse> => {
): Promise<SchemaResponse | null> => {
const { requestOpts, uri, introspectionOptions } = schemaConfig;
if (!uri) {
return null;
}
const fetchResult = await fetch(uri, {
method: requestOpts?.method ?? 'post',
body: JSON.stringify({
Expand Down
8 changes: 3 additions & 5 deletions packages/monaco-graphql/src/GraphQLWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import type { worker, editor, Position, IRange } from 'monaco-editor';
import { getRange, LanguageService } from 'graphql-language-service';

import type {
RawSchema,
SchemaResponse,
CompletionItem as GraphQLCompletionItem,
} from 'graphql-language-service';
Expand All @@ -37,20 +36,19 @@ export class GraphQLWorker {
private _formattingOptions: FormattingOptions | undefined;
constructor(ctx: worker.IWorkerContext, createData: ICreateData) {
this._ctx = ctx;
// if you must, we have a nice default schema loader at home
this._languageService = new LanguageService(createData.languageConfig);
this._formattingOptions = createData.formattingOptions;
}

async getSchemaResponse(_uri?: string): Promise<SchemaResponse> {
async getSchemaResponse(_uri?: string): Promise<SchemaResponse | null> {
return this._languageService.getSchemaResponse();
}

async setSchema(schema: RawSchema): Promise<void> {
async setSchema(schema: string): Promise<void> {
await this._languageService.setSchema(schema);
}

async loadSchema(_uri?: string): Promise<GraphQLSchema> {
async loadSchema(_uri?: string): Promise<GraphQLSchema | null> {
return this._languageService.getSchema();
}

Expand Down

0 comments on commit c8008f7

Please sign in to comment.