diff --git a/CHANGELOG.md b/CHANGELOG.md index a96b6ac..32ab97e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- 2016-05-31 - v1.2.0 +- 2016-05-31 - Use latest `jsonapi-server` processed filter - 2016-05-13 - v1.1.0 - 2016-05-13 - Support `jsonapi-server` v1.2 - 2016-05-13 - Multiple resources per database diff --git a/lib/sqlHandler.js b/lib/sqlHandler.js index 87c2e16..8a837cd 100644 --- a/lib/sqlHandler.js +++ b/lib/sqlHandler.js @@ -5,13 +5,17 @@ var async = require("async"); var crypto = require("crypto"); var debug = require("debug")("jsonApi:store:relationaldb"); var Joi = require("joi"); +var semver = require("semver"); var _ = { pick: require("lodash.pick"), assign: require("lodash.assign"), omit: require("lodash.omit") }; +var MIN_SERVER_VERSION = "1.10.0"; + var SqlStore = module.exports = function SqlStore(config) { + SqlStore._checkMinServerVersion(); this.config = config; }; @@ -22,6 +26,14 @@ SqlStore._sequelizeInstances = Object.create(null); */ SqlStore.prototype.ready = false; +SqlStore._checkMinServerVersion = function() { + var serverVersion = require('jsonapi-server')._version; + if (!serverVersion) return; + if (semver.lt(serverVersion, MIN_SERVER_VERSION)) { + throw new Error("This version of jsonapi-store-mongodb requires jsonapi-server>=" + MIN_SERVER_VERSION + "."); + } +}; + /** initialise gets invoked once for each resource that uses this hander. In this instance, we're instantiating a Sequelize instance and building models. @@ -137,6 +149,7 @@ SqlStore.prototype._joiSchemaToSequelizeModel = function(joiSchema) { if (attribute._type === "string") model[attributeName] = { type: Sequelize.STRING, allowNull: true }; if (attribute._type === "date") model[attributeName] = { type: Sequelize.STRING, allowNull: true }; if (attribute._type === "number") model[attributeName] = { type: Sequelize.INTEGER, allowNull: true }; + if (attribute._type === "boolean") model[attributeName] = { type: Sequelize.BOOLEAN, allowNull: true }; }); return model; @@ -251,35 +264,42 @@ SqlStore.prototype._filterInclude = function(relationships) { SqlStore.prototype._generateSearchBlock = function(request) { var self = this; - var attributesToFilter = _.omit(request.params.filter, Object.keys(self.relations)); - var searchBlock = self._recurseOverSearchBlock(attributesToFilter); + var attributesToFilter = _.omit(request.params.processedFilter, Object.keys(self.relations)); + var searchBlock = SqlStore._getSearchBlock(attributesToFilter); return searchBlock; }; -SqlStore.prototype._recurseOverSearchBlock = function(obj) { - var self = this; - if (!obj) return { }; +SqlStore._scalarFilterElementToWhereObj = function(element) { + var value = element.value; + if (!element.operator) return value; + var whereObj = { + ">": { $gt: value }, + "<": { $lt: value }, + "~": { $like: value }, + ":": { $like: "%" + value + "%" } + }[element.operator]; + return whereObj; +}; + +SqlStore._filterElementToSearchBlock = function(filterElement) { + if (!filterElement) return { }; + var whereObjs = filterElement.map(function(scalarFilterElement) { + return SqlStore._scalarFilterElementToWhereObj(scalarFilterElement); + }); + if (!whereObjs.length) return { }; + if (filterElement.length === 1) { + return whereObjs[0]; + } + return { $or: whereObjs }; +}; + +SqlStore._getSearchBlock = function(filter) { + if (!filter) return { }; var searchBlock = { }; - Object.keys(obj).forEach(function(attributeName) { - var textToMatch = obj[attributeName]; - if (textToMatch instanceof Array) { - searchBlock[attributeName] = { $or: textToMatch.map(function(i) { - return self._recurseOverSearchBlock({ i: i }).i; - }) }; - } else if (textToMatch instanceof Object) { - // Do nothing, its a nested filter - } else if (textToMatch[0] === ">") { - searchBlock[attributeName] = { $gt: textToMatch.substring(1) }; - } else if (textToMatch[0] === "<") { - searchBlock[attributeName] = { $lt: textToMatch.substring(1) }; - } else if (textToMatch[0] === "~") { - searchBlock[attributeName] = { $like: textToMatch.substring(1) }; - } else if (textToMatch[0] === ":") { - searchBlock[attributeName] = { $like: "%" + textToMatch.substring(1) + "%" }; - } else { - searchBlock[attributeName] = textToMatch; - } + Object.keys(filter).forEach(function(attributeName) { + var filterElement = filter[attributeName]; + searchBlock[attributeName] = SqlStore._filterElementToSearchBlock(filterElement); }); return searchBlock; diff --git a/package.json b/package.json index eacdf35..ea55b09 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jsonapi-store-relationaldb", - "version": "1.1.0", + "version": "1.2.0", "description": "Relational data store for jsonapi-server.", "keywords": [ "json:api", @@ -22,25 +22,26 @@ "node": "*" }, "dependencies": { - "async": "1.5.0", - "debug": "2.2.0", + "async": "^1.5.2", + "debug": "^2.2.0", "joi": "6.10.1", - "lodash.assign": "4.0.8", - "lodash.omit": "4.2.1", - "lodash.pick": "4.2.0", - "sequelize": "3.23.0" + "lodash.assign": "^4.0.9", + "lodash.omit": "^4.3.0", + "lodash.pick": "^4.2.1", + "semver": "^5.1.0", + "sequelize": "^3.23.3" }, "devDependencies": { "blanket": "1.1.9", - "coveralls": "2.11.9", - "eslint": "2.9.0", - "jsonapi-server": "1.2.0", - "mocha": "2.4.5", - "mocha-lcov-reporter": "1.2.0", - "mocha-performance": "0.1.1", - "mysql": "2.10.2", - "plato": "1.5.0", - "v8-profiler": "5.6.4" + "coveralls": "^2.11.9", + "eslint": "^2.11.0", + "jsonapi-server": "^1.10.0", + "mocha": "^2.5.3", + "mocha-lcov-reporter": "^1.2.0", + "mocha-performance": "^0.1.1", + "mysql": "^2.10.2", + "plato": "^1.5.0", + "v8-profiler": "^5.6.5" }, "scripts": { "test": "./setupDatabase.sh jsonapi-relationaldb-test && ./node_modules/mocha/bin/mocha --timeout 20000 -R spec ./test/*.js",