From 213b653ede495936749c2848b3dea787f9249b3b Mon Sep 17 00:00:00 2001 From: evlach Date: Sat, 3 Oct 2015 17:27:06 +0200 Subject: [PATCH 1/7] chore(cassandra-jpa): Add npm and git ignore, update package.json --- .gitignore | 11 +++++++++++ .npmignore | 21 +++++++++++++++++++++ package.json | 2 +- 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 .npmignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a2e770f --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +*.log +*.csv +*.dat +*.out +*.pid +*.gz +pids +logs +.idea +npm-debug.log +node_modules diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..e6a9168 --- /dev/null +++ b/.npmignore @@ -0,0 +1,21 @@ +.git* +lib-cov +*.seed +*.log +*.csv +*.dat +*.out +*.pid +*.gz +*.tgz + +pids +logs/ +doc/ +examples/ +test/ +.DS_Store +.idea + +npm-debug.log +node_modules diff --git a/package.json b/package.json index e642bc1..eb8bc81 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cassandra-jpa", - "version": "0.1.0", + "version": "0.4.0", "description": "Imergo Node.js Javascript Persistence API for Apache Cassandra.", "main": "index.js", "repository": { From f35c71deae920c9093dde1524437d830a0e39ef2 Mon Sep 17 00:00:00 2001 From: evlach Date: Sat, 3 Oct 2015 18:09:59 +0200 Subject: [PATCH 2/7] chore(package.json): update --- package.json | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index eb8bc81..a75e5cc 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,16 @@ "name": "Web Compliance Center", "email": "webcc@fit.fraunhofer.de" }, + "keywords": [ + "cassandra", + "cql", + "cql3", + "imergo", + "webcc", + "database", + "jpa", + "persistence" + ], "dependencies": { "async": "^1.4.2", "cassandra-driver": "^2.2.1", @@ -27,7 +37,7 @@ } }, "scripts": { - "test": "mocha -R spec -t 10000 test/**/*.test.js", + "test": "mocha -R spec -t 7000 test/**/*.test.js", "jpa:cover": "istanbul cover -x *.test.js _mocha -- -R spec test/*.js" }, "license": { @@ -37,5 +47,5 @@ "engines": { "node": ">=4.1.0" }, - "homepage": "https://github.com/webcc/typly" + "homepage": "https://github.com/webcc/cassandra-jpa" } \ No newline at end of file From 531b6e0cb1b2ddee285ce4589388c0e2b4a48b64 Mon Sep 17 00:00:00 2001 From: evlach Date: Sat, 3 Oct 2015 19:11:43 +0200 Subject: [PATCH 3/7] feat(PersistenceUtils): Add bindToJSON, rel to #15 --- lib/PersistenceUtils.js | 72 ++++++++++++++++++++++++++++++++--- test/PersistenceUtils.test.js | 19 +++++++++ 2 files changed, 85 insertions(+), 6 deletions(-) diff --git a/lib/PersistenceUtils.js b/lib/PersistenceUtils.js index 0db4674..3c34dc4 100644 --- a/lib/PersistenceUtils.js +++ b/lib/PersistenceUtils.js @@ -1,18 +1,18 @@ "use strict"; let err = require("./errors"); -let t = require("typly").instance(); +let typly = require("typly").instance(); let TimeUuid = require('cassandra-driver').types.TimeUuid; module.exports = class PersistenceUtils { static toTimeUuid(value) { - t.assertNotNull(value); - if (t.isObject(value)) + typly.assertNotNull(value); + if (typly.isObject(value)) { if (value instanceof TimeUuid) { return value; } - if (t.isDate(value)) + if (typly.isDate(value)) { return TimeUuid.fromDate(value); } @@ -21,9 +21,9 @@ module.exports = class PersistenceUtils { throw new RangeError("Invalid object"); } } - else if (t.isString(value)) + else if (typly.isString(value)) { - if (t.isUUID(value)) + if (typly.isUUID(value)) { return TimeUuid.fromString(value); } @@ -37,4 +37,64 @@ module.exports = class PersistenceUtils { throw new TypeError("Invalid type"); } } + + static clone(entity) + { + return new entity.constructor(entity); + } + + static bindToJSON(entity) + { + if (entity.toJSON !== "function") + { + entity.toJSON = function toJSON() + { + let obj = {}; + Object.keys(this).map(key => + { + if (key.charAt(0) === "_") + { + let newKey = key.substring(1); + let property = this[key]; + if (property == null) + { + obj[newKey] = null; + } + else if (typeof property === "object") + { + obj[newKey] = property; + } + else if (property instanceof Map) + { + let arr = []; + for (let pair of property) + { + let o = [pair[0], pair[1]]; + arr.push(o); + } + obj[newKey] = arr; + } + else if (Array.isArray(property)) + { + obj[newKey] = this[key]; + } + else if (property[Symbol.iterator] && typeof property === "object") + { + throw new RangeError("Unknown collection to toJSON for " + property); + } + else + { + obj[newKey] = this[key]; + } + } + else + { + obj[key] = this[key]; + } + }); + return obj; + }; + } + return entity; + } }; \ No newline at end of file diff --git a/test/PersistenceUtils.test.js b/test/PersistenceUtils.test.js index 7a5f8fb..ed3d57a 100644 --- a/test/PersistenceUtils.test.js +++ b/test/PersistenceUtils.test.js @@ -47,5 +47,24 @@ describe("cassandra-persistence", function () }, RangeError); }); }); + describe("#bindToJSON", function () + { + let entity = + { + _id: "id", + _name: "name", + echo: function echo (s) + { + return s; + } + }; + it("should bind toJSON", function () + { + let newEntity = m.PersistenceUtils.bindToJSON(entity); + let o = JSON.parse(JSON.stringify(newEntity)); + assert(typeof newEntity.toJSON === "function"); + assert.equal(o.id, newEntity._id); + }); + }); }); }); \ No newline at end of file From 0462f13fcff02a0b47baa17f15669852c47d503b Mon Sep 17 00:00:00 2001 From: evlach Date: Sat, 3 Oct 2015 19:22:48 +0200 Subject: [PATCH 4/7] feat(EntityManager): Add TTL support when ttl defined in metaModel --- examples/FooMetaModel.js | 1 + lib/CriteriaQuery.js | 12 ++++++++++-- lib/MetaModel.js | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/FooMetaModel.js b/examples/FooMetaModel.js index f501177..375f706 100644 --- a/examples/FooMetaModel.js +++ b/examples/FooMetaModel.js @@ -14,6 +14,7 @@ module.exports = class FooMetaModel extends m.MetaModel { this.clusteringColumns = new Map([["name", "ASC"]]); this.secondaryIndexes = ["name"]; this.entityClass = Foo; + this.ttl = 5000; // if not, set undefined } toRow(entity) diff --git a/lib/CriteriaQuery.js b/lib/CriteriaQuery.js index 6f5781f..7f6ea9f 100644 --- a/lib/CriteriaQuery.js +++ b/lib/CriteriaQuery.js @@ -60,8 +60,12 @@ module.exports = class CriteriaQuery { pairsQ = pairsQ + "\"" + key + "\"" + "=? "; } } + let ttlQ = ""; + if(this.metaModel.ttl){ + ttlQ = " USING TTL " + this.metaModel.ttl + " "; + } return { - query: "UPDATE " + this.getFullTableName() + " SET " + pairsQ + criteriaQuery + ";", + query: "UPDATE " + this.getFullTableName() + ttlQ + " SET " + pairsQ + criteriaQuery + ";", params: params }; } @@ -93,8 +97,12 @@ module.exports = class CriteriaQuery { paramsKeys = " (" + paramsKeys.substring(0, paramsKeys.length - 1) + ") "; let paramsQ = JSON.stringify(qq).toString().replace(/"/g, '').substring(1); paramsQ = " (" + paramsQ.substring(0, paramsQ.length - 1) + ") "; + let ttlQ = ""; + if(this.metaModel.ttl){ + ttlQ = " USING TTL " + this.metaModel.ttl + " "; + } let queryObject = { - query: "INSERT INTO " + this.getFullTableName() + paramsKeys + " VALUES " + paramsQ + " IF NOT EXISTS;", + query: "INSERT INTO " + this.getFullTableName() + paramsKeys + " VALUES " + paramsQ + " IF NOT EXISTS " + ttlQ + ";", params: params }; return (queryObject); diff --git a/lib/MetaModel.js b/lib/MetaModel.js index e26bac9..502cd15 100644 --- a/lib/MetaModel.js +++ b/lib/MetaModel.js @@ -17,6 +17,7 @@ module.exports = class MetaModel { this.secondaryIndexes = config.secondaryIndexes || []; this.entityClass = config.entityClass || Entity; this.rowInterceptor = config.rowInterceptor || new DefaultRowInterceptor(); + this.ttl = undefined; } fromRow(row) From 38342c62ddaa1b50e0a7e0f2871e4d3ccd4994e9 Mon Sep 17 00:00:00 2001 From: evlach Date: Sat, 3 Oct 2015 19:29:31 +0200 Subject: [PATCH 5/7] fix(CassandraClientFactory.test) --- test/CassandraClientFactory.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CassandraClientFactory.test.js b/test/CassandraClientFactory.test.js index 5f57726..7ed15fc 100644 --- a/test/CassandraClientFactory.test.js +++ b/test/CassandraClientFactory.test.js @@ -7,7 +7,7 @@ describe("cassandra-persistence", function () { describe("#CassandraClientFactory", function () { - let config = (new JPAConfiguration()).cassandra; + let config = require("./config/config.js").cassandra; config.contactPoints = [ process.env.DBHOST || config.contactPoints[0] ]; it("should create a client", function (done) { From 9be5e40da658d650bdc4b9efab93c8a9f0f7ea2f Mon Sep 17 00:00:00 2001 From: evlach Date: Sat, 3 Oct 2015 19:37:03 +0200 Subject: [PATCH 6/7] test(cassandra-jpa): Improve config for testing --- test/EntityManager.test.js | 1 - test/config/config.js | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/test/EntityManager.test.js b/test/EntityManager.test.js index da98a26..03a7566 100644 --- a/test/EntityManager.test.js +++ b/test/EntityManager.test.js @@ -10,7 +10,6 @@ describe("cassandra-persistence", function () describe("#EntityManager", function () { let config = require("./config/config.js"); - config.cassandra.contactPoints = [ process.env.DBHOST || config.cassandra.contactPoints[0] ]; let fooMetaModel = new FooMetaModel(config.cassandra.keyspace); let entityManager; let foo = new Foo({ diff --git a/test/config/config.js b/test/config/config.js index 7b500fe..410fa79 100644 --- a/test/config/config.js +++ b/test/config/config.js @@ -7,5 +7,7 @@ function initConfig() configuration = new jpa.JPAConfiguration(); configuration.cassandra.contactPoints = ["localhost"]; configuration.cassandra.keyspace = "tests"; + configuration.cassandra.contactPoints = [ process.env.JPA_HOST || configuration.cassandra.contactPoints[0] ]; + configuration.cassandra.keyspace = [ process.env.JPA_KEYSPACE || configuration.cassandra.keyspace ]; return configuration; } \ No newline at end of file From d3b14dc9931e62008edb2951e0d961f2c8926692 Mon Sep 17 00:00:00 2001 From: evlach Date: Sat, 3 Oct 2015 19:55:39 +0200 Subject: [PATCH 7/7] feat(EntityManager): Allow for logging Query Objects for debugging #10 --- lib/EntityManager.js | 24 ++++++++++++++++-------- lib/JPAConfiguration.js | 1 + test/config/config.js | 1 + 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/lib/EntityManager.js b/lib/EntityManager.js index 3378410..7546f4b 100644 --- a/lib/EntityManager.js +++ b/lib/EntityManager.js @@ -12,19 +12,13 @@ module.exports = class EntityManager { { JPAConfiguration.validate(config); this.metaModel = metaModel; + this.config = config; if (this.metaModel) { this.criteriaBuilder = new CriteriaBuilder(metaModel); this.criteriaQuery = this.criteriaBuilder.createQuery(); } - if (typeof config.client === "undefined" || config.client === null) - { - this.client = CassandraClientFactory.getClient(config.cassandra); - } - else - { - this.client = config.client; - } + this.client = CassandraClientFactory.getClient(config.cassandra); if (config.logger) { this.logger = config.logger; @@ -122,6 +116,7 @@ module.exports = class EntityManager { query(queryObject, callback) { + this.logQueryObject(queryObject); let self = this; this.client.execute(queryObject.query, queryObject.params, this.client.options.queryOptions, function (error, result) @@ -150,6 +145,7 @@ module.exports = class EntityManager { { self.client.batch(query, self.client.options.queryOptions, function (error, result) { + this.logQueryObject(queryObject); if (error) { Logger.error(JSON.stringify(error, null, 0)); @@ -356,4 +352,16 @@ module.exports = class EntityManager { return callback(error, result); }); } + logQueryObject(q) + { + let logQueryObject = false; + if(this.config.logQueryObject !== "undefined") + { + logQueryObject = this.config.logQueryObject; + } + if(logQueryObject) + { + Logger.info(JSON.stringify(q)); + } + } }; \ No newline at end of file diff --git a/lib/JPAConfiguration.js b/lib/JPAConfiguration.js index 4105693..4d77c49 100644 --- a/lib/JPAConfiguration.js +++ b/lib/JPAConfiguration.js @@ -40,6 +40,7 @@ module.exports = class JPAConfiguration { pooling.coreConnectionsPerHost[distance.local] = 4; pooling.coreConnectionsPerHost[distance.remote] = 1; config.logger = logger; + config.logQueryObject = false; config.cassandra = { contactPoints: ["localhost"], keyspace: "tests", diff --git a/test/config/config.js b/test/config/config.js index 410fa79..4dfb5f8 100644 --- a/test/config/config.js +++ b/test/config/config.js @@ -7,6 +7,7 @@ function initConfig() configuration = new jpa.JPAConfiguration(); configuration.cassandra.contactPoints = ["localhost"]; configuration.cassandra.keyspace = "tests"; + configuration.logQueryObject = false; configuration.cassandra.contactPoints = [ process.env.JPA_HOST || configuration.cassandra.contactPoints[0] ]; configuration.cassandra.keyspace = [ process.env.JPA_KEYSPACE || configuration.cassandra.keyspace ]; return configuration;