diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ad9653d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,145 @@ +# Change Log + +## [v2.2.0](https://github.com/feathersjs/feathers-knex/tree/v2.2.0) (2016-06-17) +[Full Changelog](https://github.com/feathersjs/feathers-knex/compare/v2.1.3...v2.2.0) + +**Merged pull requests:** + +- Update feathers-service-tests to version 0.6.0 🚀 [\#53](https://github.com/feathersjs/feathers-knex/pull/53) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) + +## [v2.1.3](https://github.com/feathersjs/feathers-knex/tree/v2.1.3) (2016-06-01) +[Full Changelog](https://github.com/feathersjs/feathers-knex/compare/v2.1.2...v2.1.3) + +**Closed issues:** + +- Support $search [\#30](https://github.com/feathersjs/feathers-knex/issues/30) +- Support for deeply nested/complex queries [\#10](https://github.com/feathersjs/feathers-knex/issues/10) +- Support population one level deep [\#3](https://github.com/feathersjs/feathers-knex/issues/3) + +**Merged pull requests:** + +- Use internal methods in create [\#51](https://github.com/feathersjs/feathers-knex/pull/51) ([t2t2](https://github.com/t2t2)) +- mocha@2.5.0 breaks build 🚨 [\#50](https://github.com/feathersjs/feathers-knex/pull/50) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) +- babel-preset-es2015@6.9.0 breaks build 🚨 [\#49](https://github.com/feathersjs/feathers-knex/pull/49) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) +- Update babel-plugin-add-module-exports to version 0.2.0 🚀 [\#47](https://github.com/feathersjs/feathers-knex/pull/47) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) +- Update knex to version 0.11.1 🚀 [\#46](https://github.com/feathersjs/feathers-knex/pull/46) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) +- feathers-rest@1.3.0 breaks build 🚨 [\#40](https://github.com/feathersjs/feathers-knex/pull/40) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) +- babel-core@6.7.6 breaks build 🚨 [\#36](https://github.com/feathersjs/feathers-knex/pull/36) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) + +## [v2.1.2](https://github.com/feathersjs/feathers-knex/tree/v2.1.2) (2016-04-01) +[Full Changelog](https://github.com/feathersjs/feathers-knex/compare/v2.1.1...v2.1.2) + +**Closed issues:** + +- Review patch and remove many for consistency [\#26](https://github.com/feathersjs/feathers-knex/issues/26) +- Feature request: ability to CRUD without needing to bind as a feathers service [\#19](https://github.com/feathersjs/feathers-knex/issues/19) + +**Merged pull requests:** + +- Update all dependencies 🌴 [\#33](https://github.com/feathersjs/feathers-knex/pull/33) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) +- fix count when specifying option.id [\#32](https://github.com/feathersjs/feathers-knex/pull/32) ([nilsboy](https://github.com/nilsboy)) + +## [v2.1.1](https://github.com/feathersjs/feathers-knex/tree/v2.1.1) (2016-02-24) +[Full Changelog](https://github.com/feathersjs/feathers-knex/compare/v2.1.0...v2.1.1) + +**Merged pull requests:** + +- bumping feathers-errors version [\#31](https://github.com/feathersjs/feathers-knex/pull/31) ([ekryski](https://github.com/ekryski)) + +## [v2.1.0](https://github.com/feathersjs/feathers-knex/tree/v2.1.0) (2016-01-31) +[Full Changelog](https://github.com/feathersjs/feathers-knex/compare/v2.0.2...v2.1.0) + +**Merged pull requests:** + +- Use internal methods instead of service methods directly [\#29](https://github.com/feathersjs/feathers-knex/pull/29) ([daffl](https://github.com/daffl)) + +## [v2.0.2](https://github.com/feathersjs/feathers-knex/tree/v2.0.2) (2016-01-25) +[Full Changelog](https://github.com/feathersjs/feathers-knex/compare/v2.0.1...v2.0.2) + +**Merged pull requests:** + +- set `returning` of `knex\#insert` to `this.id` [\#28](https://github.com/feathersjs/feathers-knex/pull/28) ([ahdinosaur](https://github.com/ahdinosaur)) + +## [v2.0.1](https://github.com/feathersjs/feathers-knex/tree/v2.0.1) (2016-01-24) +[Full Changelog](https://github.com/feathersjs/feathers-knex/compare/v2.0.0...v2.0.1) + +**Closed issues:** + +- Initialization should conform with other adapters [\#24](https://github.com/feathersjs/feathers-knex/issues/24) + +**Merged pull requests:** + +- Adding nsp check [\#27](https://github.com/feathersjs/feathers-knex/pull/27) ([marshallswain](https://github.com/marshallswain)) +- fix README example [\#25](https://github.com/feathersjs/feathers-knex/pull/25) ([ahdinosaur](https://github.com/ahdinosaur)) + +## [v2.0.0](https://github.com/feathersjs/feathers-knex/tree/v2.0.0) (2016-01-06) +[Full Changelog](https://github.com/feathersjs/feathers-knex/compare/v1.3.0...v2.0.0) + +**Closed issues:** + +- Expose Knex lib [\#23](https://github.com/feathersjs/feathers-knex/issues/23) + +## [v1.3.0](https://github.com/feathersjs/feathers-knex/tree/v1.3.0) (2015-12-21) +[Full Changelog](https://github.com/feathersjs/feathers-knex/compare/v1.2.2...v1.3.0) + +**Closed issues:** + +- Example code problems [\#20](https://github.com/feathersjs/feathers-knex/issues/20) +- README example does not work [\#18](https://github.com/feathersjs/feathers-knex/issues/18) + +**Merged pull requests:** + +- Es6 [\#22](https://github.com/feathersjs/feathers-knex/pull/22) ([ekryski](https://github.com/ekryski)) + +## [v1.2.2](https://github.com/feathersjs/feathers-knex/tree/v1.2.2) (2015-11-24) +[Full Changelog](https://github.com/feathersjs/feathers-knex/compare/v1.2.1...v1.2.2) + +**Closed issues:** + +- Example in README does not work [\#16](https://github.com/feathersjs/feathers-knex/issues/16) + +**Merged pull requests:** + +- Use CommonJS export Babel plugin [\#17](https://github.com/feathersjs/feathers-knex/pull/17) ([daffl](https://github.com/daffl)) + +## [v1.2.1](https://github.com/feathersjs/feathers-knex/tree/v1.2.1) (2015-11-23) +[Full Changelog](https://github.com/feathersjs/feathers-knex/compare/v1.2.0...v1.2.1) + +**Closed issues:** + +- Should other adapters rely on this or Sequelize? [\#14](https://github.com/feathersjs/feathers-knex/issues/14) +- Object.assign not available in older Node versions [\#13](https://github.com/feathersjs/feathers-knex/issues/13) + +**Merged pull requests:** + +- Upgrading to Babel 6 and adding Object.assign polyfill [\#15](https://github.com/feathersjs/feathers-knex/pull/15) ([daffl](https://github.com/daffl)) + +## [v1.2.0](https://github.com/feathersjs/feathers-knex/tree/v1.2.0) (2015-11-11) +[Full Changelog](https://github.com/feathersjs/feathers-knex/compare/v1.1.0...v1.2.0) + +**Merged pull requests:** + +- Upgrade to latest service tests [\#12](https://github.com/feathersjs/feathers-knex/pull/12) ([daffl](https://github.com/daffl)) + +## [v1.1.0](https://github.com/feathersjs/feathers-knex/tree/v1.1.0) (2015-11-04) +[Full Changelog](https://github.com/feathersjs/feathers-knex/compare/v1.0.0...v1.1.0) + +**Merged pull requests:** + +- Move to using feathers-service-tests for unified test suite [\#11](https://github.com/feathersjs/feathers-knex/pull/11) ([daffl](https://github.com/daffl)) + +## [v1.0.0](https://github.com/feathersjs/feathers-knex/tree/v1.0.0) (2015-10-26) +**Closed issues:** + +- Support remove [\#9](https://github.com/feathersjs/feathers-knex/issues/9) +- Support patch [\#8](https://github.com/feathersjs/feathers-knex/issues/8) +- Support update [\#7](https://github.com/feathersjs/feathers-knex/issues/7) +- Support special query filters [\#6](https://github.com/feathersjs/feathers-knex/issues/6) +- Support basic find queries [\#5](https://github.com/feathersjs/feathers-knex/issues/5) +- Support get queries [\#4](https://github.com/feathersjs/feathers-knex/issues/4) +- Support comparators, like $gte, $gt, etc. [\#2](https://github.com/feathersjs/feathers-knex/issues/2) +- Support conditional queries [\#1](https://github.com/feathersjs/feathers-knex/issues/1) + + + +\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* \ No newline at end of file diff --git a/README.md b/README.md index 272ba6b..e3f0772 100644 --- a/README.md +++ b/README.md @@ -80,31 +80,8 @@ console.log('Feathers Todo Knex service running on 127.0.0.1:3030'); You can run this example by using `node server` and going to [localhost:8080/todos](http://localhost:8080/todos). You should see an empty array. That's because you don't have any Todos yet but you now have full CRUD for your new todos service! -## Changelog - -__2.1.0__ - -- Use internal methods instead of service methods directly - -__2.0.0__ - -- Refactoring to be independent from Knex module -- Compatibility with latest common Feathers service specification - -__1.2.0__ - -- Babel 6 support, Object.assign polyfill and CommonJS module backwards compatibility - -__1.1.0__ - -- Compatibility with latest service tests - -__1.0.0__ - -- Initial release - ## License -Copyright (c) 2015 +Copyright (c) 2016 Licensed under the [MIT license](LICENSE). diff --git a/package.json b/package.json index 89707a8..af1a970 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "feathers-knex", "description": "A service plugin for KnexJS a query builder for PostgreSQL, MySQL and SQLite3", - "version": "2.2.0", + "version": "2.3.0", "homepage": "https://github.com/feathersjs/feathers-knex", "keywords": [ "feathers", @@ -32,7 +32,8 @@ "main": "lib/", "scripts": { "prepublish": "npm run compile", - "publish": "git push origin && git push origin --tags", + "publish": "git push origin --tags && npm run changelog && git push origin", + "changelog": "github_changelog_generator && git add CHANGELOG.md && git commit -am \"Updating changelog\"", "release:patch": "npm version patch && npm publish", "release:minor": "npm version minor && npm publish", "release:major": "npm version major && npm publish", @@ -47,9 +48,8 @@ "lib": "lib" }, "dependencies": { - "babel-polyfill": "^6.3.14", "feathers-errors": "^2.0.1", - "feathers-query-filters": "^1.5.1", + "feathers-query-filters": "^2.0.0", "is-plain-object": "^2.0.1", "uberproto": "^1.2.0" }, @@ -63,10 +63,10 @@ "chai": "^3.4.1", "feathers": "^2.0.0-pre.4", "feathers-rest": "^1.3.0", - "feathers-service-tests": "^0.6.0", + "feathers-service-tests": "^0.8.0", "jshint": "^2.8.0", - "knex": "^0.11.1", - "mocha": "^2.5.0", + "knex": "^0.12.0", + "mocha": "^3.0.0", "sqlite3": "^3.1.0" } } diff --git a/src/index.js b/src/index.js index 58a0553..5ab8349 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,3 @@ -if(!global._babelPolyfill) { require('babel-polyfill'); } - import Proto from 'uberproto'; import filter from 'feathers-query-filters'; import isPlainObject from 'is-plain-object'; @@ -39,6 +37,7 @@ class Service { this.id = options.id || 'id'; this.paginate = options.paginate || {}; this.table = options.name; + this.events = options.events || []; } // NOTE (EK): We need this method so that we return a new query @@ -52,23 +51,27 @@ class Service { } knexify(query, params, parentKey) { - Object.keys(params || {}).forEach((key) => { + Object.keys(params || {}).forEach(key => { const value = params[key]; if (isPlainObject(value)) { return this.knexify(query, value, key); } + // const self = this; const column = parentKey || key; const method = METHODS[key]; const operator = OPERATORS[key] || '='; - // TODO (EK): Handle $or queries with nested specials. - // Right now they won't work and we'd need to start diving - // into nested where conditions. if (method) { if (key === '$or') { - return value.forEach(condition => query[method].call(query, condition)); + const self = this; + + return value.forEach(condition => { + query[method](function() { + self.knexify(this, condition); + }); + }); } return query[method].call(query, column, value); @@ -79,36 +82,35 @@ class Service { } _find(params, count, getFilter = filter) { - let query = this.db().select(['*']); - let filters = getFilter(params.query || {}); + const { filters, query } = getFilter(params.query || {}); + let q = this.db().select(['*']); // $select uses a specific find syntax, so it has to come first. if (filters.$select) { - let fields = filters.$select; - query = this.db().select(... fields); + q = this.db().select(... filters.$select); } // build up the knex query out of the query params - this.knexify(query, params.query); + this.knexify(q, query); // Handle $sort if (filters.$sort) { Object.keys(filters.$sort).forEach(key => - query = query.orderBy(key, parseInt(filters.$sort[key], 10) === 1 ? 'asc' : 'desc')); + q = q.orderBy(key, parseInt(filters.$sort[key], 10) === 1 ? 'asc' : 'desc')); } // Handle $limit if (filters.$limit) { - query.limit(filters.$limit); + q.limit(filters.$limit); } // Handle $skip if (filters.$skip) { - query.offset(filters.$skip); + q.offset(filters.$skip); } const executeQuery = total => { - return query.then(data => { + return q.then(data => { return { total, limit: filters.$limit, @@ -121,7 +123,7 @@ class Service { if(count) { let countQuery = this.db().count(`${this.id} as total`); - this.knexify(countQuery, params.query); + this.knexify(countQuery, query); return countQuery.then(count => count[0].total).then(executeQuery); } @@ -162,8 +164,11 @@ class Service { } _create(data, params) { - return this.db().insert(data, this.id).then(rows => this._get(rows[0], params)) - .catch(errorHandler); + return this.db().insert(data, this.id).then(rows => { + const id = typeof data[this.id] !== 'undefined' ? + data[this.id] : rows[0]; + return this._get(id, params); + }).catch(errorHandler); } create(data, params) { @@ -174,21 +179,32 @@ class Service { return this._create(data, params); } - patch(id, data, params) { - params.query = params.query || {}; - data = Object.assign({}, data); + patch(id, raw, params) { + const query = Object.assign({}, params.query); + const data = Object.assign({}, raw); + const patchQuery = {}; if(id !== null) { - params.query[this.id] = id; + query[this.id] = id; } - let query = this.db(); - this.knexify(query, params.query); + // Account for potentially modified data + Object.keys(query).forEach(key => { + if(query[key] !== undefined && data[key] !== undefined && + typeof data[key] !== 'object') { + patchQuery[key] = data[key]; + } else { + patchQuery[key] = query[key]; + } + }); + + let q = this.db(); + this.knexify(q, query); delete data[this.id]; - return query.update(data).then(() => { - return this._find(params).then(page => { + return q.update(data).then(() => { + return this._find({ query: patchQuery }).then(page => { const items = page.data; if(id !== null) { diff --git a/test/index.test.js b/test/index.test.js index 07f0b13..d4d2de7 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -4,7 +4,7 @@ import { expect } from 'chai'; import assert from 'assert'; import feathers from 'feathers'; import knex from 'knex'; -import { base, orm, example } from 'feathers-service-tests'; +import { base, example } from 'feathers-service-tests'; import { errors } from 'feathers-errors'; import service from '../src'; import server from '../example/app'; @@ -16,91 +16,74 @@ const db = knex({ } }); -const app = feathers().use('/people', service({ - Model: db, - name: 'people' -})); - -let _ids = {}; -let people = app.service('people'); - -function clean(done) { - db.schema.dropTableIfExists('people').then(() => { - db.schema.createTable('people', table => { - table.increments('id'); - table.string('name'); - table.integer('age'); - table.integer('time'); - table.boolean('created'); - }) - .then(() => { - done(); - }); - }); +function clean() { + return db.schema.dropTableIfExists('people') + .then(() => db.schema.dropTableIfExists('people-customid')) + .then(() => + db.schema.createTable('people', table => { + table.increments('id'); + table.string('name'); + table.integer('age'); + table.integer('time'); + table.boolean('created'); + }).then(() => db.schema.createTable('people-customid', table => { + table.increments('customid'); + table.string('name'); + table.integer('age'); + table.integer('time'); + table.boolean('created'); + })) + ); } describe('Feathers Knex Service', () => { + const app = feathers().use('/people', service({ + Model: db, + name: 'people', + events: [ 'testing' ] + })).use('/people-customid', service({ + Model: db, + id: 'customid', + name: 'people-customid', + events: [ 'testing' ] + })); + before(clean); after(clean); describe('Initialization', () => { describe('when missing options', () => { - it('throws an error', () => { - expect(service.bind(null)).to.throw('Knex options have to be provided'); - }); + it('throws an error', () => + expect(service.bind(null)) + .to.throw('Knex options have to be provided') + ); }); describe('when missing a Model', () => { - it('throws an error', () => { - expect(service.bind(null, {})).to.throw(/You must provide a Model/); - }); + it('throws an error', () => + expect(service.bind(null, {})) + .to.throw(/You must provide a Model/) + ); }); describe('when missing a table name', () => { - it('throws an error', () => { - expect(service.bind(null, { Model: {} })).to.throw('No table name specified.'); - }); - }); - - describe('when missing the id option', () => { - it('sets the default to be id', () => { - expect(people.id).to.equal('id'); - }); - }); - - describe('when missing the paginate option', () => { - it('sets the default to be {}', () => { - expect(people.paginate).to.deep.equal({}); - }); + it('throws an error', () => + expect(service.bind(null, { Model: {} })) + .to.throw('No table name specified.') + ); }); }); describe('Common functionality', () => { - beforeEach(done => { - people.create({ - name: 'Doug', - age: 32 - }).then(data => { - _ids.Doug = data.id; - done(); - }, done); - }); + it('is CommonJS compatible', () => + assert.equal(typeof require('../lib'), 'function') + ); - afterEach(done => people.remove(_ids.Doug, {}) - .then(() => done(), () => done())); - - it('is CommonJS compatible', () => { - assert.equal(typeof require('../lib'), 'function'); - }); - - base(people, _ids, errors); + base(app, errors, 'people'); + base(app, errors, 'people-customid', 'customid'); }); }); -describe.skip('Knex service ORM errors', () => { - orm(people, _ids, errors); -}); - describe('Knex service example test', () => { after(done => server.close(() => done()));