diff --git a/fetch/karma.conf.js b/fetch/karma.conf.js index 866b314..c7a02e7 100644 --- a/fetch/karma.conf.js +++ b/fetch/karma.conf.js @@ -52,6 +52,7 @@ module.exports = function (config) { browsers: browsers, files: [ 'node_modules/babel-polyfill/dist/polyfill.js', + 'node_modules/whatwg-fetch/fetch.js', 'node_modules/js-data/dist/js-data.js', 'fetch/dist/js-data-fetch.js', 'fetch/karma.start.js', diff --git a/fetch/karma.start.js b/fetch/karma.start.js index acaa197..60bf631 100644 --- a/fetch/karma.start.js +++ b/fetch/karma.start.js @@ -1,6 +1,8 @@ /* global JSData:true, JSDataHttp:true, sinon:true, chai:true */ + before(function () { var Test = this + Test.TEST_FETCH = true Test.fail = function (msg) { if (msg instanceof Error) { console.log(msg.stack) @@ -14,7 +16,14 @@ before(function () { } Test.sinon = sinon Test.JSData = JSData + Test.addAction = JSDataHttp.addAction + Test.addActions = JSDataHttp.addActions Test.HttpAdapter = JSDataHttp.HttpAdapter + + Test.store = new JSData.DataStore() + Test.adapter = new Test.HttpAdapter() + Test.store.registerAdapter('http', Test.adapter, { default: true }) + Test.User = new JSData.Mapper({ name: 'user' }) @@ -24,14 +33,13 @@ before(function () { basePath: 'api' }) + Test.User.registerAdapter('http', Test.adapter, { default: true }) + Test.Post.registerAdapter('http', Test.adapter, { default: true }) console.log('Testing against js-data ' + JSData.version.full) }) beforeEach(function () { var Test = this - Test.adapter = new Test.HttpAdapter() - Test.User.registerAdapter('http', Test.adapter, { default: true }) - Test.Post.registerAdapter('http', Test.adapter, { default: true }) Test.p1 = { author: 'John', age: 30, id: 5 } Test.p2 = { author: 'Sally', age: 31, id: 6 } diff --git a/fetch/package.json b/fetch/package.json index ca72193..f050e4e 100644 --- a/fetch/package.json +++ b/fetch/package.json @@ -18,8 +18,7 @@ "axios", "rest", "adapter", - "http", - "fetch" + "http" ], "dependencies": { "js-data-adapter": "~0.8.1" diff --git a/karma.conf.js b/karma.conf.js index bd3db1e..f2b9408 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -52,6 +52,7 @@ module.exports = function (config) { browsers: browsers, files: [ 'node_modules/babel-polyfill/dist/polyfill.js', + 'node_modules/whatwg-fetch/fetch.js', 'node_modules/js-data/dist/js-data.js', 'dist/js-data-http.js', 'karma.start.js', diff --git a/karma.start.js b/karma.start.js index 464dc71..908c422 100644 --- a/karma.start.js +++ b/karma.start.js @@ -1,6 +1,8 @@ /* global JSData:true, JSDataHttp:true, sinon:true, chai:true */ + before(function () { var Test = this + Test.TEST_FETCH = false Test.fail = function (msg) { if (msg instanceof Error) { console.log(msg.stack) @@ -14,7 +16,15 @@ before(function () { } Test.sinon = sinon Test.JSData = JSData + Test.addAction = JSDataHttp.addAction + Test.addActions = JSDataHttp.addActions Test.HttpAdapter = JSDataHttp.HttpAdapter + + Test.store = new JSData.DataStore() + Test.adapter = new Test.HttpAdapter() + + Test.store.registerAdapter('http', Test.adapter, { default: true }) + Test.User = new JSData.Mapper({ name: 'user' }) @@ -24,14 +34,13 @@ before(function () { basePath: 'api' }) + Test.User.registerAdapter('http', Test.adapter, { default: true }) + Test.Post.registerAdapter('http', Test.adapter, { default: true }) console.log('Testing against js-data ' + JSData.version.full) }) beforeEach(function () { var Test = this - Test.adapter = new Test.HttpAdapter() - Test.User.registerAdapter('http', Test.adapter, { default: true }) - Test.Post.registerAdapter('http', Test.adapter, { default: true }) Test.p1 = { author: 'John', age: 30, id: 5 } Test.p2 = { author: 'Sally', age: 31, id: 6 } diff --git a/node/mocha.start.js b/node/mocha.start.js index cbbcc6f..f0f9d27 100644 --- a/node/mocha.start.js +++ b/node/mocha.start.js @@ -18,7 +18,13 @@ before(function () { } Test.sinon = require('sinon') Test.JSData = require('js-data') + Test.addAction = require('./dist/js-data-http-node').addAction + Test.addActions = require('./dist/js-data-http-node').addActions Test.HttpAdapter = require('./dist/js-data-http-node').HttpAdapter + Test.store = new Test.JSData.DataStore() + Test.adapter = new Test.HttpAdapter() + Test.store.registerAdapter('http', Test.adapter, { default: true }) + Test.User = new Test.JSData.Mapper({ name: 'user' }) @@ -28,15 +34,13 @@ before(function () { basePath: 'api' }) + Test.User.registerAdapter('http', Test.adapter, { default: true }) + Test.Post.registerAdapter('http', Test.adapter, { default: true }) console.log('Testing against js-data ' + Test.JSData.version.full) }) beforeEach(function () { var Test = this - Test.adapter = new Test.HttpAdapter() - Test.User.registerAdapter('http', Test.adapter, { default: true }) - Test.Post.registerAdapter('http', Test.adapter, { default: true }) - Test.p1 = { author: 'John', age: 30, id: 5 } Test.p2 = { author: 'Sally', age: 31, id: 6 } Test.p3 = { author: 'Mike', age: 32, id: 7 } diff --git a/package.json b/package.json index 815dc97..263ed63 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,7 @@ "phantomjs-prebuilt": "2.1.12", "rollup-plugin-commonjs": "3.3.1", "rollup-plugin-replace": "1.1.1", - "uglify-js": "2.7.0" + "uglify-js": "2.7.0", + "whatwg-fetch": "^1.0.0" } } diff --git a/src/index.js b/src/index.js index 4ca18a7..724d33d 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,4 @@ -/* global fetch:true Headers:true Request:true */ +/* global fetch:true Headers:true */ import {utils} from 'js-data' import axios from '../node_modules/axios/dist/axios' @@ -53,7 +53,7 @@ function buildUrl (url, params) { } val.forEach(function (v) { - if (window.toString.call(v) === '[object Date]') { + if (toString.call(v) === '[object Date]') { v = v.toISOString() } else if (utils.isObject(v)) { v = utils.toJson(v) @@ -558,20 +558,19 @@ Adapter.extend({ * @param {Object} config.headers Headers for the request. * @param {Object} config.params Querystring for the request. * @param {string} config.url Url for the request. - * @param {Object} [opts] Configuration options. */ - fetch (config, opts) { + fetch (config) { const requestConfig = { method: config.method, // turn the plain headers object into the Fetch Headers object - headers: new Headers(config.headers) + headers: new Headers(config.headers || {}) } if (config.data) { requestConfig.body = utils.toJson(config.data) } - return fetch(new Request(buildUrl(config.url, config.params), requestConfig)) + return fetch(buildUrl(config.url, config.params), requestConfig) .then((response) => { response.config = { method: config.method, @@ -969,7 +968,7 @@ Adapter.extend({ sum (mapper, field, query, opts) { query || (query = {}) opts || (opts = {}) - if (!utils.utils.isString(field)) { + if (!utils.isString(field)) { throw new Error('field must be a string!') } opts.params = this.getParams(opts) @@ -1064,7 +1063,7 @@ Adapter.extend({ * * // GET /reports/schools/:school_id/teachers * addAction('getTeacherReports', { - * basePath: 'reports/schools', + * endpoint: 'reports/schools', * pathname: 'teachers', * method: 'GET' * })(store.getMapper('school')) @@ -1098,22 +1097,22 @@ export function addAction (name, opts) { opts.response = opts.response || function (response) { return response } opts.responseError = opts.responseError || function (err) { return utils.reject(err) } mapper[name] = function (id, _opts) { + _opts = _opts || {} if (utils.isObject(id)) { _opts = id } - _opts = _opts || {} - let adapter = this.getAdapter(opts.adapter || this.defaultAdapter || 'http') - let config = {} - utils.fillIn(config, opts) - if (!_opts.hasOwnProperty('endpoint') && config.endpoint) { - _opts.endpoint = config.endpoint - } + utils.fillIn(_opts, opts) + let adapter = this.getAdapter(_opts.adapter || this.defaultAdapter || 'http') + const config = {} + config.mapper = this.name + utils.deepMixIn(config, _opts) + config.method = config.method || 'GET' if (typeof _opts.getEndpoint === 'function') { config.url = _opts.getEndpoint(this, _opts) } else { let args = [ _opts.basePath || this.basePath || adapter.basePath, - adapter.getEndpoint(this, utils.isSorN(id) ? id : null, _opts) + adapter.getEndpoint(this, id, _opts) ] if (utils.isSorN(id)) { args.push(id) @@ -1121,11 +1120,8 @@ export function addAction (name, opts) { args.push(opts.pathname || name) config.url = makePath.apply(null, args) } - config.method = config.method || 'GET' - config.mapper = this.name - utils.deepMixIn(config, _opts) return utils.resolve(config) - .then(_opts.request || opts.request) + .then(_opts.request) .then((config) => adapter.HTTP(config)) .then((data) => { if (data && data.config) { @@ -1133,7 +1129,7 @@ export function addAction (name, opts) { } return data }) - .then(_opts.response || opts.response, _opts.responseError || opts.responseError) + .then(_opts.response, _opts.responseError) } return mapper } diff --git a/test/count.test.js b/test/count.test.js new file mode 100644 index 0000000..c360e4f --- /dev/null +++ b/test/count.test.js @@ -0,0 +1,14 @@ +describe('sum', function () { + it('should include count=true in query_params', function (done) { + var Test = this + + setTimeout(function () { + Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, '{"count": 5}') + }, 5) + + Test.adapter.count(Test.Post).then(function (result) { + Test.assert.equal(Test.requests[0].url, 'api/posts?count=true') + done() + }) + }) +}) diff --git a/test/createMany.test.js b/test/createMany.test.js index 1066bc7..870a756 100644 --- a/test/createMany.test.js +++ b/test/createMany.test.js @@ -1,3 +1,17 @@ describe('createMany', function () { - it('should createMany') + it('should createMany', function (done) { + var Test = this + + setTimeout(function () { + Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, '') + }, 5) + + var many = [{ author_id: 2, text: 'bar' }, { author_id: 2, text: 'foo' }] + Test.Post.createMany(many).then(function (result) { + Test.assert.equal(Test.requests[0].url, 'api/posts') + Test.assert.equal(Test.requests[0].method, 'POST') + Test.assert.equal(Test.requests[0].requestBody, JSON.stringify(many)) + done() + }) + }) }) diff --git a/test/fetch.test.js b/test/fetch.test.js index d86e69f..523ec58 100644 --- a/test/fetch.test.js +++ b/test/fetch.test.js @@ -1,3 +1,36 @@ describe('fetch', function () { - it('should fetch') + it('should fetch from a URL', function (done) { + var Test = this + // Don't test fetch for Node + try { + fetch // eslint-disable-line + } catch (e) { + return this.skip() + } + if (Test.TEST_FETCH) { + Test.xhr = Test.sinon.useFakeXMLHttpRequest() + Test.requests = [] + Test.xhr.onCreate = function (xhr) { + Test.requests.push(xhr) + } + } + + setTimeout(function () { + Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, '{}') + }, 300) + + Test.adapter.fetch({ + method: 'get', + params: { active: true }, + url: '/api/foos' + }).then(function (response) { + var request = Test.requests[0] + Test.assert.equal(request.method, 'GET') + Test.assert.equal(request.url, '/api/foos?active=true') + if (Test.TEST_FETCH) { + Test.xhr.restore() + } + done() + }) + }) }) diff --git a/test/static.addAction.test.js b/test/static.addAction.test.js index a368b20..2e993e1 100644 --- a/test/static.addAction.test.js +++ b/test/static.addAction.test.js @@ -1,3 +1,57 @@ describe('static addAction', function () { - it('should addAction') + it('should addAction', function (done) { + var Test = this + var SchoolMapper = Test.store.defineMapper('school', {}) + + // GET async/reports/schools/:school_id/teachers + Test.addAction('getTeacherReportsAsync', { + basePath: 'async/', + endpoint: 'reports/schools', + pathname: 'teachers', + method: 'GET' + })(SchoolMapper) + + setTimeout(function () { + Test.requests[0].respond(200, { 'Content-Type': 'text/plain' }, '') + }, 5) + + SchoolMapper.getTeacherReportsAsync(1234).then(function (response) { + Test.assert.equal(1, Test.requests.length) + Test.assert.equal(Test.requests[0].url, 'async/reports/schools/1234/teachers', 'Add action configures basePath, endpoint and pathname') + Test.assert.equal(Test.requests[0].method, 'GET') + done() + }) + }) + + it('addAction action is callable with params instead of id', function (done) { + var Test = this + var adapter = Test.adapter + var store = new Test.JSData.DataStore() + store.registerAdapter('http', adapter, { default: true }) + var SchoolMapper = store.defineMapper('school', {}) + + // GET async/reports/schools/teachers + Test.addAction('getAllTeacherReportsAsync', { + basePath: 'async/', + endpoint: 'reports/schools', + pathname: 'teachers', + method: 'GET' + })(SchoolMapper) + + setTimeout(function () { + Test.requests[0].respond(200, { 'Content-Type': 'text/plain' }, '') + }, 5) + + // GET async/reports/schools/teachers?= + SchoolMapper.getAllTeacherReportsAsync({ + params: { + subject: 'esperanto' + } + }).then(function (response) { + Test.assert.equal(1, Test.requests.length) + Test.assert.equal(Test.requests[0].url, 'async/reports/schools/teachers?subject=esperanto', 'Add action configures basePath, endpoint, pathname, and querystring') + Test.assert.equal(Test.requests[0].method, 'GET') + done() + }) + }) }) diff --git a/test/static.addActions.test.js b/test/static.addActions.test.js index 685c0cf..38a0930 100644 --- a/test/static.addActions.test.js +++ b/test/static.addActions.test.js @@ -1,3 +1,48 @@ describe('static addActions', function () { - it('should addActions') + it('should addActions', function (done) { + var Test = this + var SchoolMapper = Test.store.defineMapper('school', {}) + + // GET async/reports/schools/:school_id/teachers + Test.addActions({ + getTeacherReports: { + endpoint: 'reports/schools', + pathname: 'teachers', + method: 'GET' + }, + getStudentReports: { + endpoint: 'reports/schools', + pathname: 'students', + method: 'GET' + } + })(SchoolMapper) + + var asyncTestOne = function (nextTest) { + setTimeout(function () { + Test.requests[0].respond(200, { 'Content-Type': 'text/plain' }, '') + }, 5) + + SchoolMapper.getTeacherReports(1234).then(function (response) { + Test.assert.equal(1, Test.requests.length) + Test.assert.equal(Test.requests[0].url, 'reports/schools/1234/teachers', 'Add action configures basePath, endpoint and pathname') + Test.assert.equal(Test.requests[0].method, 'GET') + nextTest() + }) + } + + var asyncTestTwo = function () { + setTimeout(function () { + Test.requests[1].respond(200, { 'Content-Type': 'text/plain' }, '') + }, 5) + + SchoolMapper.getStudentReports(1234).then(function (response) { + Test.assert.equal(2, Test.requests.length) + Test.assert.equal(Test.requests[1].url, 'reports/schools/1234/students', 'Add action configures basePath, endpoint and pathname') + Test.assert.equal(Test.requests[1].method, 'GET') + done() + }) + } + + asyncTestOne(asyncTestTwo) + }) }) diff --git a/test/sum.test.js b/test/sum.test.js new file mode 100644 index 0000000..273e7e7 --- /dev/null +++ b/test/sum.test.js @@ -0,0 +1,14 @@ +describe('sum', function () { + it('should sum= in query_params', function (done) { + var Test = this + + setTimeout(function () { + Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, '{"sum": 5}') + }, 5) + + Test.adapter.sum(Test.Post, 'num_views').then(function (result) { + Test.assert.equal(Test.requests[0].url, 'api/posts?sum=num_views') + done() + }) + }) +}) diff --git a/test/updateMany.test.js b/test/updateMany.test.js index c1137a6..8a2763b 100644 --- a/test/updateMany.test.js +++ b/test/updateMany.test.js @@ -1,3 +1,17 @@ -describe('updateMany', function () { - it('should updateMany') +describe('createMany', function () { + it('should createMany', function (done) { + var Test = this + var many = [{ author_id: 2, text: 'bar', id: 1 }, { author_id: 2, text: 'foo', id: 2 }] + + setTimeout(function () { + Test.requests[0].respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(many)) + }, 5) + + Test.Post.updateMany(many).then(function (result) { + Test.assert.equal(Test.requests[0].url, 'api/posts') + Test.assert.equal(Test.requests[0].method, 'PUT') + Test.assert.equal(Test.requests[0].requestBody, JSON.stringify(many)) + done() + }) + }) })