-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from skitsanos/graphql_support
feat: GraphQL support [beta]
- Loading branch information
Showing
10 changed files
with
239 additions
and
73 deletions.
There are no files selected for viewing
This file contains 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,16 @@ | ||
POST {{URL}}/examples/graphql_demo | ||
|
||
```graphql | ||
{ | ||
hello | ||
|
||
getArticle(_key: "3704"){ | ||
title, | ||
author {name} | ||
}, | ||
|
||
getAllArticles { | ||
title | ||
} | ||
} | ||
``` |
This file contains 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 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 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 |
---|---|---|
@@ -1,2 +1,3 @@ | ||
/.idea/ | ||
/node_modules/ | ||
/node_modules/ | ||
*.lock |
This file contains 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 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 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 |
---|---|---|
|
@@ -5,8 +5,11 @@ | |
* @author Skitsanos, [email protected], https://github.com/skitsanos | ||
*/ | ||
const createRouter = require('@arangodb/foxx/router'); | ||
const createGraphQLRouter = require('@arangodb/foxx/graphql'); | ||
const graphql = require('graphql'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const {graphqlSync} = require('graphql'); | ||
|
||
const getServicesBase = () => | ||
{ | ||
|
@@ -26,9 +29,10 @@ const getServicesBase = () => | |
process.exit(1); | ||
}; | ||
|
||
const supportedMethods = ['all', 'get', 'post', 'put', 'delete', 'patch', 'head', 'options', 'trace', 'graphql']; | ||
|
||
const index = { | ||
foxxServicesLocation: getServicesBase(), | ||
supportedMethods: ['all', 'get', 'post', 'put', 'delete', 'patch', 'head'], | ||
|
||
assignParams(type, params, endpoint) | ||
{ | ||
|
@@ -91,47 +95,87 @@ const index = { | |
if (fs.isFile(fullPath)) | ||
{ | ||
const method = path.basename(fullPath, '.js'); | ||
if (this.supportedMethods.includes(method)) | ||
|
||
if (supportedMethods.includes(method)) | ||
{ | ||
const temp = fullPath.split(this.foxxServicesLocation)[1].split(`${method}.js`)[0]; | ||
const pathToHandle = temp.substring(0, temp.length - 1).replace(/\\/gi, '/'); | ||
const m = require(fullPath); | ||
const routeHandlerModule = require(fullPath); | ||
|
||
//parse path params | ||
const pathParsed = pathToHandle.replace(/\$/gi, ':'); | ||
|
||
//create endpoint handler | ||
// | ||
// create endpoint handler for the GraphQL | ||
// | ||
if (method === 'graphql') | ||
{ | ||
if (!('schema' in routeHandlerModule)) | ||
{ | ||
const err = `GraphQL schema is not defined for ${pathParsed}`; | ||
console.error(err); | ||
throw new Error(err); | ||
} | ||
|
||
module.context.use(pathParsed, createGraphQLRouter({ | ||
graphql, | ||
formatError: (error) => | ||
{ | ||
return { | ||
meta: { | ||
platform: 'foxx-builder' | ||
}, | ||
message: error.message, | ||
locations: error.locations, | ||
path: error.path | ||
}; | ||
}, | ||
executor: ({context, document, variables}) => | ||
graphqlSync({ | ||
schema: routeHandlerModule.schema, | ||
contextValue: context, | ||
source: document, | ||
variableValues: variables | ||
}), | ||
...routeHandlerModule | ||
})); | ||
return; | ||
} | ||
|
||
// | ||
// create endpoint handler for the HTTP verbs | ||
// | ||
const r = createRouter(); | ||
const endpoint = r[method](pathParsed, m.handler); | ||
const endpoint = r[method](pathParsed, routeHandlerModule.handler); | ||
|
||
//check if params were defined | ||
if (Object.prototype.hasOwnProperty.call(m, 'params')) | ||
if (Object.prototype.hasOwnProperty.call(routeHandlerModule, 'params')) | ||
{ | ||
//check for path params | ||
if (Object.prototype.hasOwnProperty.call(m.params, 'path')) | ||
if (Object.prototype.hasOwnProperty.call(routeHandlerModule.params, 'path')) | ||
{ | ||
this.assignParams('path', m.params.path, endpoint); | ||
this.assignParams('path', routeHandlerModule.params.path, endpoint); | ||
} | ||
|
||
//check for query params | ||
if (Object.prototype.hasOwnProperty.call(m.params, 'query')) | ||
if (Object.prototype.hasOwnProperty.call(routeHandlerModule.params, 'query')) | ||
{ | ||
this.assignParams('query', m.params.query, endpoint); | ||
this.assignParams('query', routeHandlerModule.params.query, endpoint); | ||
} | ||
} | ||
|
||
//check headers | ||
if (Object.prototype.hasOwnProperty.call(m, 'headers')) | ||
if (Object.prototype.hasOwnProperty.call(routeHandlerModule, 'headers')) | ||
{ | ||
this.assignParams('headers', m.headers, endpoint); | ||
this.assignParams('headers', routeHandlerModule.headers, endpoint); | ||
} | ||
|
||
//check for body defs | ||
if (Object.prototype.hasOwnProperty.call(m, 'body')) | ||
if (Object.prototype.hasOwnProperty.call(routeHandlerModule, 'body')) | ||
{ | ||
if (Boolean(m.body)) | ||
if (Boolean(routeHandlerModule.body)) | ||
{ | ||
const {model, mimes, description} = m.body; | ||
const {model, mimes, description} = routeHandlerModule.body; | ||
endpoint.body(model, mimes, description); | ||
} | ||
else | ||
|
@@ -140,9 +184,9 @@ const index = { | |
} | ||
} | ||
|
||
if (Object.prototype.hasOwnProperty.call(m, 'error') && Array.isArray(m.error)) | ||
if (Object.prototype.hasOwnProperty.call(routeHandlerModule, 'error') && Array.isArray(routeHandlerModule.error)) | ||
{ | ||
for (const rule of m.error) | ||
for (const rule of routeHandlerModule.error) | ||
{ | ||
const [status, message] = Object.entries(rule)[0]; | ||
endpoint.error(Number(status), message); | ||
|
This file contains 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 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,97 @@ | ||
const {GraphQLString, GraphQLSchema, GraphQLObjectType, GraphQLList} = require('graphql'); | ||
const {query} = require('@arangodb'); | ||
|
||
const AuthorType = new GraphQLObjectType({ | ||
name: 'Author', | ||
fields: { | ||
_key: {type: GraphQLString}, | ||
name: {type: GraphQLString} | ||
}, | ||
resolve: (_, author) => | ||
{ | ||
const result = query` | ||
FOR author IN Authors | ||
FILTER author._key == ${author._key} | ||
RETURN author | ||
`; | ||
return result.next(); | ||
} | ||
}); | ||
|
||
const ArticleType = new GraphQLObjectType({ | ||
name: 'Article', | ||
fields: { | ||
_key: {type: GraphQLString}, | ||
title: {type: GraphQLString}, | ||
authorKey: {type: GraphQLString}, | ||
author: { | ||
type: AuthorType, | ||
resolve: (parent) => | ||
{ | ||
const authorKey = parent.authorKey; | ||
const result = query` | ||
FOR author IN Authors | ||
FILTER author._key == ${authorKey} | ||
RETURN author | ||
`; | ||
return result.next(); | ||
} | ||
} | ||
}, | ||
resolve: (_, article) => | ||
{ | ||
const result = query` | ||
FOR author IN Authors | ||
FILTER author._key == ${article.authorKey}} | ||
RETURN author | ||
`; | ||
return result.next(); | ||
} | ||
}); | ||
|
||
module.exports = { | ||
schema: new GraphQLSchema({ | ||
query: new GraphQLObjectType({ | ||
name: 'RootQueryType', | ||
fields: { | ||
hello: { | ||
type: GraphQLString, | ||
resolve() | ||
{ | ||
return 'world'; | ||
} | ||
}, | ||
|
||
getArticle: { | ||
type: ArticleType, | ||
|
||
args: { | ||
_key: {type: GraphQLString} | ||
}, | ||
|
||
resolve(_, {_key}) | ||
{ | ||
const result = query`FOR article IN Articles | ||
FILTER article._key == ${_key} | ||
RETURN article`; | ||
return result.next(); | ||
} | ||
|
||
}, | ||
|
||
getAllArticles: { | ||
type: new GraphQLList(ArticleType), | ||
resolve() | ||
{ | ||
const result = query` | ||
FOR article IN Articles | ||
RETURN article | ||
`; | ||
return result.toArray(); | ||
} | ||
} | ||
} | ||
}) | ||
}), | ||
graphiql: true | ||
}; |
Oops, something went wrong.