This repository was archived by the owner on Apr 14, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
GraphiQL server (+ minor usability features) #112
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
<html> | ||
<head> | ||
<style> | ||
body { | ||
height: 100%; | ||
margin: 0; | ||
width: 100%; | ||
overflow: hidden; | ||
} | ||
#graphiql { | ||
height: 100vh; | ||
} | ||
</style> | ||
|
||
<script src="https://unpkg.com/react@15/dist/react.js"></script> | ||
<script src="https://unpkg.com/react-dom@15/dist/react-dom.js"></script> | ||
|
||
<!-- | ||
These two files can be found in the npm module, however you may wish to | ||
copy them directly into your environment, or perhaps include them in your | ||
favored resource bundler. | ||
--> | ||
<link rel="stylesheet" href="/static/css/graphiql.css" /> | ||
<script src="/static/js/graphiql.js"></script> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you forget to add these files into the commit? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. Sorry. Added them. |
||
|
||
</head> | ||
<body> | ||
<div id="graphiql">Loading...</div> | ||
<script> | ||
/** | ||
* This GraphiQL example illustrates how to use some of GraphiQL's props | ||
* in order to enable reading and updating the URL parameters, making | ||
* link sharing of queries a little bit easier. | ||
* | ||
* This is only one example of this kind of feature, GraphiQL exposes | ||
* various React params to enable interesting integrations. | ||
*/ | ||
// Parse the search string to get url parameters. | ||
var search = window.location.search; | ||
var parameters = {}; | ||
search.substr(1).split('&').forEach(function (entry) { | ||
var eq = entry.indexOf('='); | ||
if (eq >= 0) { | ||
parameters[decodeURIComponent(entry.slice(0, eq))] = | ||
decodeURIComponent(entry.slice(eq + 1)); | ||
} | ||
}); | ||
// if variables was provided, try to format it. | ||
if (parameters.variables) { | ||
try { | ||
parameters.variables = | ||
JSON.stringify(JSON.parse(parameters.variables), null, 2); | ||
} catch (e) { | ||
// Do nothing, we want to display the invalid JSON as a string, rather | ||
// than present an error. | ||
} | ||
} | ||
// When the query and variables string is edited, update the URL bar so | ||
// that it can be easily shared | ||
function onEditQuery(newQuery) { | ||
parameters.query = newQuery; | ||
updateURL(); | ||
} | ||
function onEditVariables(newVariables) { | ||
parameters.variables = newVariables; | ||
updateURL(); | ||
} | ||
function onEditOperationName(newOperationName) { | ||
parameters.operationName = newOperationName; | ||
updateURL(); | ||
} | ||
function updateURL() { | ||
var newSearch = '?' + Object.keys(parameters).filter(function (key) { | ||
return Boolean(parameters[key]); | ||
}).map(function (key) { | ||
return encodeURIComponent(key) + '=' + | ||
encodeURIComponent(parameters[key]); | ||
}).join('&'); | ||
history.replaceState(null, null, newSearch); | ||
} | ||
// Defines a GraphQL fetcher using the fetch API. You're not required to | ||
// use fetch, and could instead implement graphQLFetcher however you like, | ||
// as long as it returns a Promise or Observable. | ||
function graphQLFetcher(graphQLParams) { | ||
// This example expects a GraphQL server at the path /graphql. | ||
// Change this to point wherever you host your GraphQL server. | ||
return fetch('/graphql', { | ||
method: 'post', | ||
headers: { | ||
'Accept': 'application/json', | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify(graphQLParams), | ||
credentials: 'include', | ||
}).then(function (response) { | ||
return response.text(); | ||
}).then(function (responseBody) { | ||
try { | ||
return JSON.parse(responseBody); | ||
} catch (error) { | ||
return responseBody; | ||
} | ||
}); | ||
} | ||
// Render <GraphiQL /> into the body. | ||
// See the README in the top level of this module to learn more about | ||
// how you can customize GraphiQL by providing different values or | ||
// additional child elements. | ||
ReactDOM.render( | ||
React.createElement(GraphiQL, { | ||
fetcher: graphQLFetcher, | ||
query: parameters.query, | ||
variables: parameters.variables, | ||
operationName: parameters.operationName, | ||
onEditQuery: onEditQuery, | ||
onEditVariables: onEditVariables, | ||
onEditOperationName: onEditOperationName | ||
}), | ||
document.getElementById('graphiql') | ||
); | ||
</script> | ||
</body> | ||
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
local fio = require('fio') | ||
local utils = require('graphql.server.utils') | ||
local json = require('json') | ||
local check = require('graphql.utils').check | ||
|
||
local server = {} | ||
|
||
local default_charset = "utf-8" | ||
|
||
local function file_mime_type(filename) | ||
if string.endswith(filename, ".css") then | ||
return string.format("text/css; charset=%s", default_charset) | ||
elseif string.endswith(filename, ".js") then | ||
return string.format("application/javascript; charset=%s", default_charset) | ||
elseif string.endswith(filename, ".html") then | ||
return string.format("text/html; charset=%s", default_charset) | ||
elseif string.endswith(filename, ".svg") then | ||
return string.format("image/svg+xml") | ||
end | ||
|
||
return "application/octet-stream" | ||
end | ||
|
||
local function static_handler(req) | ||
local path = req.path | ||
|
||
if path == '/' then | ||
path = fio.pathjoin('graphiql', 'index.html') | ||
end | ||
|
||
local lib_dir = utils.script_path() | ||
path = fio.pathjoin(lib_dir, path) | ||
local body = utils.read_file(path) | ||
|
||
return { | ||
status = 200, | ||
headers = { | ||
['content-type'] = file_mime_type(path) | ||
}, | ||
body = body | ||
} | ||
end | ||
|
||
function server.init(graphql, host, port) | ||
local host = host or '127.0.0.1' | ||
local port = port or 8080 | ||
local httpd = require('http.server').new(host, port) | ||
|
||
local function api_handler(req) | ||
local body = req:read() | ||
|
||
if body == nil or body == '' then | ||
return { | ||
status = 200, | ||
body = json.encode( | ||
{errors = {{message = "Expected a non-empty request body"}}} | ||
) | ||
} | ||
end | ||
|
||
local parsed = json.decode(body) | ||
if parsed == nil then | ||
return { | ||
status = 200, | ||
body = json.encode( | ||
{errors = {{message = "Body should be a valid JSON"}}} | ||
) | ||
} | ||
end | ||
|
||
if type(parsed) ~= 'table' then | ||
return { | ||
status = 200, | ||
body = json.encode( | ||
{errors = {message = "Body should be a dictionary"}} | ||
) | ||
} | ||
end | ||
|
||
if parsed.query == nil or type(parsed.query) ~= "string" then | ||
return { | ||
status = 200, | ||
body = json.encode( | ||
{errors = {{message = "Body should have 'query' field"}}} | ||
) | ||
} | ||
end | ||
|
||
local variables = parsed.variables | ||
if variables == nil then | ||
variables = {} | ||
end | ||
|
||
if type(variables) == 'cdata' then | ||
if variables == nil then | ||
variables = {} | ||
end | ||
end | ||
|
||
local query = parsed.query | ||
|
||
local ok, compiled_query = pcall(graphql.compile, graphql, query) | ||
if not ok then | ||
return { | ||
status = 200, | ||
body = json.encode({errors = {{message = compiled_query}}}) | ||
} | ||
end | ||
|
||
local ok, result = pcall(compiled_query.execute, compiled_query, variables) | ||
if not ok then | ||
return { | ||
status = 200, | ||
body = json.encode({error = {{message = result}}}) | ||
} | ||
end | ||
|
||
result = {data = result} | ||
return { | ||
status = 200, | ||
headers = { | ||
[ 'content-type'] = "application/json; charset=utf-8" | ||
}, | ||
body = json.encode(result) | ||
} | ||
end | ||
|
||
httpd:route({ path = '/' }, static_handler) | ||
httpd:route({ path = '/static/css/graphiql.css' }, static_handler) | ||
httpd:route({ path = '/static/js/graphiql.js' }, static_handler) | ||
httpd:route({ path = '/graphql' }, api_handler) | ||
|
||
return httpd | ||
end | ||
|
||
return server |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No space format at all, got:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And with format too.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What was the case? I added a test with user-configured tarantool graphql instance + server usage and it passed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I need to try again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. The problem was with global variables in grahql/core
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to track such problems: found problem with empty variables (it is converted to box.NULL and box.NULL or {} is not {}).