Skip to content

Commit 18b66bd

Browse files
committed
Improved graphene playground with router query params
1 parent 3c308b9 commit 18b66bd

File tree

6 files changed

+386
-250
lines changed

6 files changed

+386
-250
lines changed

docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"copy-webpack-plugin": "^0.2.0",
1818
"es6-promise": "^3.0.2",
1919
"extract-text-webpack-plugin": "^0.9.1",
20-
"gatsby": "^0.7.2",
20+
"gatsby": "^0.7.3",
2121
"graphiql": "^0.4.2",
2222
"graphql": "^0.4.13",
2323
"jeet": "^6.1.2",

docs/playground/GraphenePlayground.js

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
import { RouteHandler, Link, State } from 'react-router';
4+
import CodeMirror from 'codemirror';
5+
import { graphql } from 'graphql';
6+
import GraphiQL from 'graphiql';
7+
import schema from './schema';
8+
import pypyjs_vm from 'pypyjs';
9+
10+
import 'codemirror/mode/python/python';
11+
import 'codemirror/addon/lint/lint';
12+
import '../css/playground.styl';
13+
14+
if (typeof PUBLIC_PATH === "undefined") {
15+
var PUBLIC_PATH = '';
16+
}
17+
18+
pypyjs_vm.rootURL = `${PUBLIC_PATH}/playground/lib/`;
19+
pypyjs_vm.cacheKey = 'graphene';
20+
21+
CodeMirror.registerHelper('lint', 'python', function (text, options, editor) {
22+
return (options.errors || []).map((error) => {
23+
var tokens = editor.getLineTokens(error.line - 1);
24+
tokens = tokens.filter((token, pos) => {
25+
return !!token.type || token.string.trim().length > 0;
26+
});
27+
if (!tokens) return [];
28+
return {
29+
message: `${error.name}: ${error.message}`,
30+
severity: 'error',
31+
type: 'syntax',
32+
from: CodeMirror.Pos(error.line - 1, tokens[0].start),
33+
to: CodeMirror.Pos(error.line - 1, tokens[tokens.length-1].end),
34+
};
35+
});
36+
});
37+
38+
function graphQLFetcher(graphQLParams) {
39+
return graphql(schema, graphQLParams.query);
40+
}
41+
42+
var default_interpreter;
43+
class Playground extends React.Component {
44+
constructor() {
45+
super();
46+
this.state = {pypyjs: false, stdout: '', response:''};
47+
}
48+
stdout() {
49+
console.log('stdout', arguments);
50+
}
51+
componentDidMount() {
52+
if (default_interpreter) {
53+
this.pypy_interpreter = default_interpreter;
54+
this.pypy_interpreter.stdout = this.stdout.bind(this);
55+
}
56+
else {
57+
this.pypy_interpreter = new pypyjs_vm({
58+
stdin: function(){},
59+
stdout: this.stdout.bind(this),
60+
stderr: function(){},
61+
rootURL: `${PUBLIC_PATH}/playground/lib/`
62+
});
63+
default_interpreter = this.pypy_interpreter;
64+
}
65+
66+
this.pypyjs = this.pypy_interpreter.ready().then(() => {
67+
return this.pypy_interpreter.exec(`
68+
import graphene
69+
import js
70+
from collections import OrderedDict
71+
from graphql.core.execution.executor import Executor
72+
from graphql.core.execution.middlewares.sync import SynchronousExecutionMiddleware
73+
from graphql.core.error import GraphQLError, format_error
74+
75+
def get_wrapped(f):
76+
if hasattr(f, 'func_closure') and f.func_closure:
77+
return get_wrapped(f.func_closure[0].cell_contents)
78+
return f
79+
80+
class TrackResolver(SynchronousExecutionMiddleware):
81+
@staticmethod
82+
def run_resolve_fn(resolver, original_resolver):
83+
if resolver.func.__module__ == '__main__':
84+
line = get_wrapped(resolver.func).resolver.func_code.co_firstlineno
85+
js.globals.markLine(line-3)
86+
return SynchronousExecutionMiddleware.run_resolve_fn(resolver, original_resolver)
87+
88+
__graphene_executor = Executor([TrackResolver()], map_type=OrderedDict)
89+
`);
90+
}).then(() => {
91+
this.createSchema(this.props.initialSchema);
92+
}).then(() => {
93+
this.setState({pypyjs: true, response:'"Execute the query for see the results"'});
94+
});
95+
96+
window.markLine = (lineNo) => {
97+
this.markLine(lineNo);
98+
}
99+
100+
this.editor = CodeMirror(ReactDOM.findDOMNode(this.refs.schemaCode), {
101+
value: this.props.initialSchema,
102+
mode: "python",
103+
theme: "graphene",
104+
lineNumbers: true,
105+
tabSize: 4,
106+
indentUnit: 4,
107+
gutters: ["CodeMirror-linenumbers", "breakpoints"],
108+
lint: {
109+
errors: [],
110+
},
111+
});
112+
this.editor.on("change", this.onEditorChange.bind(this));
113+
}
114+
onEditorChange() {
115+
if (this.changeTimeout) {
116+
clearTimeout(this.changeTimeout);
117+
}
118+
if (this.props.onEditSchema) {
119+
var value = this.editor.getValue();
120+
if (value != this.props.initialSchema) {
121+
this.props.onEditSchema(value)
122+
}
123+
}
124+
125+
this.changeTimeout = setTimeout(() =>
126+
this.updateSchema()
127+
, 300);
128+
}
129+
updateSchema() {
130+
this.createSchema(this.editor.getValue());
131+
}
132+
createSchema(code) {
133+
if (this.previousCode == code) return;
134+
console.log('createSchema');
135+
this.validSchema = null;
136+
this.pypyjs.then(() => {
137+
return this.pypy_interpreter.exec(`
138+
schema = None
139+
${code}
140+
assert schema, 'You have to define a schema'
141+
`)
142+
}).then(() => {
143+
console.log('NO ERRORS');
144+
this.removeErrors();
145+
this.validSchema = true;
146+
}, (err) => {
147+
this.editor.options.lint.errors = [];
148+
console.log('ERRORS', err);
149+
this.logError(err);
150+
this.validSchema = false;
151+
}).then(this.updateGraphiQL.bind(this));
152+
this.previousCode = code;
153+
}
154+
updateGraphiQL() {
155+
if (this.validSchema) {
156+
this.refs.graphiql.state.schema = null;
157+
this.refs.graphiql.componentDidMount();
158+
this.refs.graphiql.forceUpdate();
159+
this.refs.graphiql.refs.docExplorer.forceUpdate();
160+
}
161+
}
162+
logError(error) {
163+
var lines = error.trace.split('\n');
164+
var file_errors = lines.map((errorLine) => {
165+
return errorLine.match(/File "<string>", line (\d+)/);
166+
}).filter((x) => !! x);
167+
if (!file_errors.length) return;
168+
var line = parseInt(file_errors[file_errors.length-1][1]);
169+
error.line = line-2;
170+
if (error.name == "ImportError" && error.message == "No module named django") {
171+
error.message = "Django is not supported yet in Playground editor";
172+
}
173+
this.editor.options.lint.errors.push(error);
174+
CodeMirror.signal(this.editor, 'change', this.editor);
175+
}
176+
removeErrors() {
177+
this.editor.options.lint.errors = [];
178+
CodeMirror.signal(this.editor, 'change', this.editor);
179+
}
180+
fetcher (graphQLParams) {
181+
if (!this.validSchema) {
182+
return graphQLFetcher(arguments);
183+
}
184+
return this.execute(graphQLParams.query);
185+
}
186+
execute(query) {
187+
// console.log('execute', query);
188+
return this.pypyjs.then(() => {
189+
var x = `
190+
import json
191+
result = __graphene_executor.execute(schema.schema, '''${query}''')
192+
result_dict = {};
193+
if result.errors:
194+
result_dict['errors'] = [format_error(e) for e in result.errors]
195+
if result.data:
196+
result_dict['data'] = result.data
197+
result_json = json.dumps(result_dict)
198+
`;
199+
return this.pypy_interpreter.exec(x)
200+
}
201+
).then(() =>
202+
this.pypy_interpreter.get(`result_json`)
203+
).then((data) => {
204+
var json_data = JSON.parse(data);
205+
return json_data;
206+
});
207+
}
208+
markLine(lineNo) {
209+
console.log(lineNo);
210+
var hlLine = this.editor.addLineClass(lineNo, "text", "activeline");
211+
// var mark = this.editor.markText({line: lineNo, ch: 0}, {line: lineNo, ch: 10}, {className: "called-function"});
212+
setTimeout(() => {
213+
this.editor.removeLineClass(lineNo, "text", "activeline");
214+
}, 1200);
215+
}
216+
render() {
217+
return (
218+
<div className="playground">
219+
{!this.state.pypyjs?<div className="loading" />:null}
220+
<div className="playground-schema">
221+
<header className="playground-schema-header">
222+
Schema
223+
</header>
224+
<div className="playground-schema-editor" ref="schemaCode" />
225+
</div>
226+
<div className="playground-graphiql">
227+
<GraphiQL ref="graphiql" fetcher={this.fetcher.bind(this)} response={this.state.response} onEditQuery={this.props.onEditQuery} query={this.props.initialQuery}/>
228+
</div>
229+
</div>
230+
);
231+
}
232+
}
233+
234+
module.exports = Playground;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
query {
2+
hello
3+
ping(to:"Peter")
4+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import graphene
2+
3+
class Query(graphene.ObjectType):
4+
hello = graphene.String()
5+
ping = graphene.String(to=graphene.String())
6+
7+
def resolve_hello(self, args, info):
8+
return 'World'
9+
10+
def resolve_ping(self, args, info):
11+
return 'Pinging {}'.format(args.get('to'))
12+
13+
schema = graphene.Schema(query=Query)

docs/playground/graphene-js/pypyjs.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,6 @@ if (typeof FunctionPromise === "undefined") {
6060
throw "FunctionPromise object not found";
6161
}
6262

63-
// Some extra goodies for nodejs.
64-
if (typeof process !== 'undefined') {
65-
if (Object.prototype.toString.call(process) === '[object process]') {
66-
var fs = require("fs");
67-
var path = require("path");
68-
}
69-
}
7063

7164
// Create functions for handling default stdio streams.
7265
// These will be shared by all VM instances by default.

0 commit comments

Comments
 (0)