Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Further fixes on pull #2 #3

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
MOCHA_TARGET=test/specs.js
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove Makefile
use npm scripts
https://www.keithcirkel.co.uk/how-to-use-npm-as-a-build-tool/


test:
make testonly && make lint

testonly:
mocha $(MOCHA_TARGET)

testonly-watch:
mocha -w $(MOCHA_TARGET)

lint:
standard .

.PHONY: test testonly testonly-watch lint
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,23 @@ npm install graphql-client -S

## How To
```javascript
var client = require('graphql-client')({url: 'http://your-host/graphql'})
var client = require('graphql-client')({
url: 'http://your-host/graphql'
})
// Before request hook
.on('request', (req) => {
// Do whatever you want with `req`, e.g. add JWT auth header
req.headers.set('Authentication', 'Bearer ' + token)
})
// On response hook. Access `Response` instance before parsing response body
.on('response', (res) => {
...
})
// After response is parsed as JSON
.on('data', (data) => {
console.log('GraphQL response:', data)
})


var query = `
query search ($query: String, $from: Int, $limit: Int) {
Expand Down
144 changes: 97 additions & 47 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,111 @@
function highlightQuery (query, errors) {
var locations = errors.map(function (e) { return e.locations })
.reduce(function (a, b) {
return a.concat(b)
}, [])
/* global fetch, Headers */
require('isomorphic-fetch')

var queryHighlight = ''
function Client (options) {
if (!options.url) throw new Error('Missing url parameter')

query.split('\n').forEach(function (row, index) {
var line = index + 1
var lineErrors = locations.filter(function (loc) { return loc.line === line })
this.options = options
this.url = options.url
// A stack of registered listeners
this.listeners = []
}

// to reduce file size
var proto = Client.prototype

/**
* Send a query and get a Promise
* @param {String} query
* @param {Object} variables
* @param {Function} beforeRequest hook
* @returns {Promise}
*/
proto.query = function (query, variables, beforeRequest) {
var self = this

queryHighlight += row + '\n'
var headers = new Headers()
headers.set('content-type', 'application/json')

if (lineErrors.length) {
var errorHighlight = []
var req = self.options.request || {}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should make this to an actual request object Fetch API Request.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! Fixed this as well

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to switch back to plain req object since there were many problems with Request. For example, I couldn't pass headers to it when initializing the client because they could only be modified after the Request object is initialized. Also, I couldn't pass "credentials" to it, because of a bug in isomorphic-fetch. There were some other issues like a difference in behaviour between browser and Node, so it is just not the way to go

req.method || (req.method = 'POST')
req.body || (req.body = JSON.stringify({
query: query,
variables: variables
}))
req.headers || (req.headers = headers)

lineErrors.forEach(function (line) {
for (var i = 0; i < 8; i++) {
errorHighlight[line.column + i] = '~'
}
})
var result = beforeRequest && beforeRequest(req)

for (var i = 0; i < errorHighlight.length; i++) {
queryHighlight += errorHighlight[i] || ' '
}
queryHighlight += '\n'
var results = self.trigger('request', req)
results.push(result)

// The 'request' or `beforeRequest` hooks may redefine response when
// returning something
for (var i = results.length; i--;) {
if (typeof results[i] !== 'undefined') {
self.trigger('data', results[i])
return Promise.resolve(results[i])
}
}

return self.fetch(req)
}

/**
* For making requests
* @param {Object} req
* @returns Promise
*/
proto.fetch = function (req) {
var self = this

return fetch(self.url, req).then(function (res) {
self.trigger('response', res)
return res.json()
}).then(function (data) {
self.trigger('data', data)
return data
})
}

/**
* Register a listener.
* @param {String} eventName - 'request', 'response', 'data'
* @param {Function} callback
* @returns Client instance
*/
proto.on = function (eventName, callback) {
var allowedNames = ['request', 'response', 'data']

return queryHighlight
if (~allowedNames.indexOf(eventName)) {
this.listeners.push([ eventName, callback ])
}

return this
}

module.exports = function (params) {
require('isomorphic-fetch')
if (!params.url) throw new Error('Missing url parameter')

return {
query: function (query, variables) {
var headers = new Headers()
headers.append('Content-Type', 'application/json')

return fetch(params.url, {
method: 'POST',
body: JSON.stringify({
query: query,
variables: variables
}),
headers: headers,
credentials: params.credentials
}).then(function (res) {
return res.json()
}).then(function (data) {
if (data.errors && data.errors.length) {
throw new Error(data.errors.map(function (e) { return e.message }).join('\n') + '\n' + highlightQuery(query, data.errors))
}
return data
})
/**
* Trigger an event.
* @param {String} eventName - 'request', 'response', 'data'
* @param {mixed} ...args
* @returns {Array} array of results received from each listener respectively
*/
proto.trigger = function (eventName) {
var args = Array.prototype.slice.call(arguments, 1)
var listeners = this.listeners
var results = []

for (var i = 0; i < listeners.length; i++) {
if (listeners[i][0] === eventName) {
results.push(listeners[i][1].apply(this, args))
}
}

return results
}

module.exports = function (options) {
return new Client(options)
}

module.exports.Client = Client
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "make test"
},
"files": [
"index.js",
"README.md"
],
"repository": {
"type": "git",
"url": "https://github.com/nordsimon/graphql-client"
Expand All @@ -15,5 +19,9 @@
"license": "ISC",
"dependencies": {
"isomorphic-fetch": "^2.2.1"
},
"devDependencies": {
"chai": "^3.5.0",
"graphql": "^0.6.2"
}
}
36 changes: 36 additions & 0 deletions test/lib/schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const {
GraphQLSchema,
GraphQLObjectType,
GraphQLString
} = require('graphql')

const data = [
{ id: '1', name: 'Dan' },
{ id: '2', name: 'Marie' },
{ id: '3', name: 'Jessie' }
]

const userType = new GraphQLObjectType({
name: 'User',
fields: {
id: { type: GraphQLString },
name: { type: GraphQLString }
}
})

const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: userType,
args: {
id: { type: GraphQLString }
},
resolve: (_, args) => data.find((u) => u.id === args.id)
}
}
})
})

module.exports = schema
54 changes: 54 additions & 0 deletions test/lib/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const http = require('http')
const schema = require('./schema')
const { graphql } = require('graphql')

module.exports = http.createServer((req, res) => {
if (req.url === '/graphql') {
let body = ''

req.on('data', function (data) {
body += data
})

req.on('end', function () {
let query = body
let variables
let operationName

if (~req.headers['content-type'].indexOf('application/json')) {
try {
const obj = JSON.parse(query)
if (obj.query && typeof obj.query === 'string') {
query = obj.query
}
if (obj.variables !== undefined) {
variables = obj.variables
}
// Name of GraphQL operation to execute.
if (typeof obj.operationName === 'string') {
operationName = obj.operationName
}
} catch (err) {
// do nothing
}
}

res.writeHead(200, {'content-type': 'text/json'})

graphql(schema, query, null, variables, operationName).then((result) => {
let response = result

if (result.errors) {
res.statusCode = 400
response = {
errors: result.errors.map(String)
}
}

res.end(JSON.stringify(response))
}).catch((e) => {
res.end(JSON.stringify(e))
})
})
}
})
Loading