diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..1130cc1 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015", "stage-2"] +} \ No newline at end of file diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..efb3491 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,214 @@ +{ + "parser": "babel-eslint", + + "plugins": [ + "babel" + ], + + "env": { + "es6": true, + "node": true + }, + + "ecmaFeatures": { + "arrowFunctions": true, + "binaryLiterals": true, + "blockBindings": true, + "classes": true, + "defaultParams": true, + "destructuring": true, + "experimentalObjectRestSpread": true, + "forOf": true, + "generators": true, + "globalReturn": true, + "jsx": true, + "modules": true, + "objectLiteralComputedProperties": true, + "objectLiteralDuplicateProperties": true, + "objectLiteralShorthandMethods": true, + "objectLiteralShorthandProperties": true, + "octalLiterals": true, + "regexUFlag": true, + "regexYFlag": true, + "restParams": true, + "spread": true, + "superInFunctions": true, + "templateStrings": true, + "unicodeCodePointEscapes": true + }, + + "rules": { + "babel/arrow-parens": [2, "as-needed"], + + "array-bracket-spacing": [2, "always"], + "arrow-spacing": 2, + "block-scoped-var": 0, + "brace-style": [2, "1tbs", {"allowSingleLine": true}], + "callback-return": 2, + "camelcase": [2, {"properties": "always"}], + "comma-dangle": 0, + "comma-spacing": 0, + "comma-style": [2, "last"], + "complexity": 0, + "computed-property-spacing": [2, "never"], + "consistent-return": 0, + "consistent-this": 0, + "curly": [2, "all"], + "default-case": 0, + "dot-location": [2, "property"], + "dot-notation": 0, + "eol-last": 2, + "eqeqeq": 2, + "func-names": 0, + "func-style": 0, + "generator-star-spacing": [0, {"before": true, "after": false}], + "guard-for-in": 2, + "handle-callback-err": [2, "error"], + "id-length": 0, + "id-match": [2, "^(?:_?[a-zA-Z0-9]*)|[_A-Z0-9]+$"], + "indent": [2, 2, {"SwitchCase": 1}], + "init-declarations": 0, + "key-spacing": [2, {"beforeColon": false, "afterColon": true}], + "linebreak-style": 2, + "lines-around-comment": 0, + "max-depth": 0, + "max-len": [2, 80, 4], + "max-nested-callbacks": 0, + "max-params": 0, + "max-statements": 0, + "new-cap": 0, + "new-parens": 2, + "newline-after-var": 0, + "no-alert": 2, + "no-array-constructor": 2, + "no-bitwise": 0, + "no-caller": 2, + "no-catch-shadow": 0, + "no-class-assign": 2, + "no-cond-assign": 2, + "no-console": 1, + "no-const-assign": 2, + "no-constant-condition": 2, + "no-continue": 0, + "no-control-regex": 0, + "no-debugger": 1, + "no-delete-var": 2, + "no-div-regex": 2, + "no-dupe-args": 2, + "no-dupe-keys": 2, + "no-duplicate-case": 2, + "no-else-return": 2, + "no-empty": 2, + "no-empty-character-class": 2, + "no-empty-label": 2, + "no-eq-null": 0, + "no-eval": 2, + "no-ex-assign": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-extra-boolean-cast": 2, + "no-extra-parens": 0, + "no-extra-semi": 2, + "no-fallthrough": 2, + "no-floating-decimal": 2, + "no-func-assign": 2, + "no-implicit-coercion": 2, + "no-implied-eval": 2, + "no-inline-comments": 0, + "no-inner-declarations": [2, "functions"], + "no-invalid-regexp": 2, + "no-invalid-this": 0, + "no-irregular-whitespace": 2, + "no-iterator": 2, + "no-label-var": 2, + "no-labels": 0, + "no-lone-blocks": 2, + "no-lonely-if": 2, + "no-loop-func": 0, + "no-mixed-requires": [2, true], + "no-mixed-spaces-and-tabs": 2, + "no-multi-spaces": 2, + "no-multi-str": 2, + "no-multiple-empty-lines": 0, + "no-native-reassign": 0, + "no-negated-in-lhs": 2, + "no-nested-ternary": 0, + "no-new": 2, + "no-new-func": 0, + "no-new-object": 2, + "no-new-require": 2, + "no-new-wrappers": 2, + "no-obj-calls": 2, + "no-octal": 2, + "no-octal-escape": 2, + "no-param-reassign": 2, + "no-path-concat": 2, + "no-plusplus": 0, + "no-process-env": 0, + "no-process-exit": 0, + "no-proto": 2, + "no-redeclare": 2, + "no-regex-spaces": 2, + "no-restricted-modules": 0, + "no-return-assign": 2, + "no-script-url": 2, + "no-self-compare": 0, + "no-sequences": 2, + "no-shadow": 2, + "no-shadow-restricted-names": 2, + "no-spaced-func": 2, + "no-sparse-arrays": 2, + "no-sync": 2, + "no-ternary": 0, + "no-this-before-super": 2, + "no-throw-literal": 2, + "no-trailing-spaces": 2, + "no-undef": 2, + "no-undef-init": 2, + "no-undefined": 0, + "no-underscore-dangle": 0, + "no-unexpected-multiline": 2, + "no-unneeded-ternary": 2, + "no-unreachable": 2, + "no-unused-expressions": 2, + "no-unused-vars": [2, {"vars": "all", "args": "after-used"}], + "no-use-before-define": 0, + "no-useless-call": 2, + "no-var": 0, + "no-void": 2, + "no-warning-comments": 0, + "no-with": 2, + "object-curly-spacing": [0, "always"], + "object-shorthand": [2, "always"], + "one-var": [2, "never"], + "operator-assignment": [2, "always"], + "operator-linebreak": [2, "after"], + "padded-blocks": 0, + "prefer-const": 0, + "prefer-reflect": 0, + "prefer-spread": 0, + "quote-props": [2, "as-needed"], + "quotes": [2, "single"], + "radix": 2, + "require-yield": 2, + "semi": [2, "always"], + "semi-spacing": [2, {"before": false, "after": true}], + "sort-vars": 0, + "space-after-keywords": [2, "always"], + "space-before-blocks": [2, "always"], + "space-before-function-paren": [2, {"anonymous": "always", "named": "never"}], + "space-in-parens": 0, + "space-infix-ops": [2, {"int32Hint": false}], + "space-return-throw-case": 2, + "space-unary-ops": [2, {"words": true, "nonwords": false}], + "spaced-comment": [2, "always"], + "strict": 0, + "use-isnan": 2, + "valid-jsdoc": 0, + "valid-typeof": 2, + "vars-on-top": 0, + "wrap-iife": 2, + "wrap-regex": 0, + "yoda": [2, "never", {"exceptRange": true}] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe8ad59 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.swp +*~ +*.iml +.*.haste_cache.* +.DS_Store +.idea +npm-debug.log +node_modules +dist \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..48769a4 --- /dev/null +++ b/.npmignore @@ -0,0 +1,8 @@ +*.swp +*~ +*.iml +.*.haste_cache.* +.DS_Store +.idea +npm-debug.log +lib \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2ae9b84 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Kadira Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1674391 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# npm-base + +A base package for creating NPM packages with ES2015. + +--- + +Writing in ES2015 is an amazing experience. Setting up babel and the development environment in a kind of a pain. + +If you want to write a **NPM module** in ES2015 and publish to NPM with backward compatibility, this is the **easiest** way. + +## Basic Usage + +* Simply clone [this](https://github.com/kadirahq/npm-base) project. +* Change the `package.json` as you want. +* `lib/index.js` in your entry point. +* Then publish to npm via `npm publish`. + +## Linting + +* ESLINT support is added to the project. +* It's configured for ES2015 and inherited configurations from [graphql/graphql-js](https://github.com/graphql/graphql-js). +* Use `npm run lint` to lint your code and `npm run lintfix` to fix common issues. + +## Testing + +* You can write test under `__test__` directory anywhere inside `lib` including sub-directories. +* Then run `npm test` to test your code. (It'll lint your code as well). +* You can also run `npm run testonly` to run tests without linting. + +## ES2015 Setup + +* ES2015 support is added with babel6. +* After you publish your project to NPM, it can be run on older node versions and browsers without the support of Babel. +* This project uses ES2015 and some of the upcoming features like `async await`. +* You can change them with adding and removing [presets](http://jamesknelson.com/the-six-things-you-need-to-know-about-babel-6/). +* All the polyfills you use are taken from the local `babel-runtime` package. So, this package won't add any global polyfills and pollute the global namespace. + +## Kudos + +* Babel6 and the team behind it. +* Facebook's [graphql-js](https://github.com/graphql/graphql-js) authors for ESLint configurations and for the directory structure. diff --git a/index.js b/index.js new file mode 100644 index 0000000..b91eb38 --- /dev/null +++ b/index.js @@ -0,0 +1 @@ +module.exports = require('./dist/index'); \ No newline at end of file diff --git a/lib/__tests__/index.js b/lib/__tests__/index.js new file mode 100644 index 0000000..5ea4f69 --- /dev/null +++ b/lib/__tests__/index.js @@ -0,0 +1,72 @@ +/* eslint max-len:0 */ + +import {describe, it} from 'mocha'; +import {expect} from 'chai'; +import LokkaHTTPTransport from '../'; +import Schema from './sample_schema'; +import express from 'express'; +import expressGraphql from 'express-graphql'; +import {promisify} from 'bluebird'; +import {findAPortNotInUse} from 'portscanner'; +const pickPort = () => promisify(findAPortNotInUse)(10000, 50000, '127.0.0.1'); + +describe('LokkaHTTPTransport', () => { + describe('constructor()', () => { + describe('without an endpoint', () => { + it('should throw an error', () => { + expect(() => new LokkaHTTPTransport()).to.throw(/endpoint is required!/); + }); + }); + + describe('with an endpoint', () => { + it('should create an instance', () => { + const transport = new LokkaHTTPTransport('/graphql'); + expect(transport.endpoint).to.be.equal('/graphql'); + }); + }); + }); + + describe('send()', () => { + describe('with a correct graphql query', () => { + it('should return the result', async () => { + const app = express(); + app.use('/graphql', expressGraphql({schema: Schema})); + const port = await pickPort(); + await promisify(app.listen.bind(app))(port); + + const transport = new LokkaHTTPTransport(`http://localhost:${port}/graphql`); + const result = await transport.send(` + { + echo(message: "Hello") + } + `); + + expect(result).to.be.deep.equal({ + echo: 'Echo: Hello' + }); + }); + }); + + describe('with an incorrect graphql query', () => { + it('should return the error', async () => { + const app = express(); + app.use('/graphql', expressGraphql({schema: Schema})); + const port = await pickPort(); + await promisify(app.listen.bind(app))(port); + + const transport = new LokkaHTTPTransport(`http://localhost:${port}/graphql`); + try { + await transport.send(` + { + echo(messa: "Hello") + } + `); + throw new Error('Some Other Error'); + } catch (err) { + expect(err.message).to.match(/GraphQL Error:/); + expect(err.rawError).to.be.instanceOf(Array); + } + }); + }); + }); +}); diff --git a/lib/__tests__/sample_schema.js b/lib/__tests__/sample_schema.js new file mode 100644 index 0000000..0f9da8c --- /dev/null +++ b/lib/__tests__/sample_schema.js @@ -0,0 +1,24 @@ +import { + GraphQLObjectType, + GraphQLString, + GraphQLSchema +} from 'graphql'; + +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'MyQuery', + fields: () => ({ + echo: { + type: GraphQLString, + args: { + message: {type: GraphQLString} + }, + resolve: (root, {message}) => { + return `Echo: ${message}`; + } + } + }) + }) +}); + +export default schema; diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..c2756ab --- /dev/null +++ b/lib/index.js @@ -0,0 +1,39 @@ +import LokkaTransport from 'lokka/transport'; +import fetch from 'isomorphic-fetch'; + +export default class LokkaHTTPTransport extends LokkaTransport { + constructor(endpoint) { + if (!endpoint) { + throw new Error('endpoint is required!'); + } + + super(); + this.endpoint = endpoint; + } + + send(query, variables, operationName) { + const payload = {query, variables, operationName}; + return fetch(this.endpoint, { + method: 'post', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify(payload), + // To pass cookies to the server. (supports CORS as well) + credentials: 'include', + }).then(response => { + return response.json(); + }).then(({data, errors}) => { + if (errors) { + const message = errors[0].message; + const error = new Error(`GraphQL Error: ${message}`); + error.rawError = errors; + + throw error; + } + + return data; + }); + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..a12b8cc --- /dev/null +++ b/package.json @@ -0,0 +1,46 @@ +{ + "name": "lokka-transport-http", + "version": "1.0.0", + "description": "HTTP Transport for Lokka", + "repository": { + "type": "git", + "url": "https://github.com/kadirahq/lokka-http-transport.git" + }, + "license": "MIT", + "options": { + "mocha": "--require scripts/mocha_runner lib/**/__tests__/**/*.js" + }, + "scripts": { + "prepublish": ". ./scripts/prepublish.sh", + "lint": "eslint ./lib", + "lintfix": "eslint ./lib --fix", + "testonly": "mocha $npm_package_options_mocha", + "test": "npm run lint && npm run testonly" + }, + "devDependencies": { + "mocha": "2.x.x", + "chai": "3.x.x", + "eslint": "1.7.x", + "babel-eslint": "4.x.x", + "eslint-plugin-babel": "2.x.x", + "babel-cli": "6.x.x", + "babel-core": "6.x.x", + "babel-polyfill": "6.x.x", + "babel-preset-es2015": "6.x.x", + "babel-preset-stage-2": "6.x.x", + "babel-plugin-transform-runtime": "6.x.x", + "portscanner": "1.x.x", + "express": "4.x.x", + "express-graphql": "0.4.x", + "graphql": "0.4.x", + "bluebird": "2.x.x", + "lokka": "1.x.x" + }, + "dependencies": { + "babel-runtime": "6.x.x", + "isomorphic-fetch": "2.x.x" + }, + "peerDependencies": { + "lokka": "1.x.x" + } +} diff --git a/scripts/mocha_runner.js b/scripts/mocha_runner.js new file mode 100755 index 0000000..16e530d --- /dev/null +++ b/scripts/mocha_runner.js @@ -0,0 +1,7 @@ +require('babel-core/register'); +require('babel-polyfill'); + +process.on('unhandledRejection', function (error) { + console.error('Unhandled Promise Rejection:'); + console.error(error && error.stack || error); +}); diff --git a/scripts/prepublish.sh b/scripts/prepublish.sh new file mode 100644 index 0000000..2a680d3 --- /dev/null +++ b/scripts/prepublish.sh @@ -0,0 +1,5 @@ +echo "> Start transpiling ES2015" +echo "" +./node_modules/.bin/babel --plugins "transform-runtime" lib --ignore __tests__ --out-dir ./dist +echo "" +echo "> Complete transpiling ES2015" \ No newline at end of file