diff --git a/.kuzzlerc b/.kuzzlerc index df567beaa3..ec335fe9a3 100644 --- a/.kuzzlerc +++ b/.kuzzlerc @@ -52,8 +52,10 @@ "admin": { "_id": "admin", "indexes": { + "_canCreate": true, "*": { "collections": { + "_canCreate": true, "*": { "controllers": { "*": { @@ -70,8 +72,10 @@ "guest": { "_id": "guest", "indexes": { + "_canCreate": true, "*": { "collections": { + "_canCreate": true, "*": { "controllers": { "*": { diff --git a/features/AMQP.feature b/features/AMQP.feature index 1b6cfe80f2..d4365951b2 100644 --- a/features/AMQP.feature +++ b/features/AMQP.feature @@ -14,6 +14,7 @@ Feature: Test AMQP API When I write the document Then I should receive a document id Then I'm able to get the document + And I'm not able to get the document in index "index-test-alt" @usingAMQP @unsubscribe Scenario: Create or Update a document @@ -39,7 +40,8 @@ Feature: Test AMQP API @usingAMQP Scenario: Search a document When I write the document "documentGrace" - Then I find a document with "grace" in field "firstName" + And I find a document with "grace" in field "firstName" + And I don't find a document with "grace" in field "firstName" in index "index-test-alt" @usingAMQP Scenario: Bulk import @@ -64,6 +66,7 @@ Feature: Test AMQP API When I write the document "documentGrace" When I write the document "documentAda" Then I count 4 documents + And I count 0 documents in index "index-test-alt" And I count 2 documents with "NYC" in field "city" Then I truncate the collection And I count 0 documents @@ -196,4 +199,11 @@ Feature: Test AMQP API Then In my list there is a collection "kuzzle-collection-test" with 2 room and 2 subscriber When I remove the first room And I get the list subscriptions - Then In my list there is a collection "kuzzle-collection-test" with 1 room and 1 subscriber \ No newline at end of file + Then In my list there is a collection "kuzzle-collection-test" with 1 room and 1 subscriber + + @usingAMQP + Scenario: create additional index + When I create an index named "my-new-index" + Then I'm able to find the index named "my-new-index" in index list + Then I'm not able to find the index named "my-undefined-index" in index list + Then I'm able to delete the index named "my-new-index" \ No newline at end of file diff --git a/features/MQTT.feature b/features/MQTT.feature index 4b38cfda39..18ff66eb26 100644 --- a/features/MQTT.feature +++ b/features/MQTT.feature @@ -14,6 +14,7 @@ Feature: Test MQTT API When I write the document Then I should receive a document id Then I'm able to get the document + And I'm not able to get the document in index "index-test-alt" @usingMQTT @unsubscribe Scenario: Create or Update a document @@ -40,6 +41,7 @@ Feature: Test MQTT API Scenario: Search a document When I write the document "documentGrace" Then I find a document with "grace" in field "firstName" + And I don't find a document with "grace" in field "firstName" in index "index-test-alt" @usingMQTT Scenario: Bulk import @@ -64,6 +66,7 @@ Feature: Test MQTT API When I write the document "documentGrace" When I write the document "documentAda" Then I count 4 documents + And I count 0 documents in index "index-test-alt" And I count 2 documents with "NYC" in field "city" Then I truncate the collection And I count 0 documents @@ -196,4 +199,11 @@ Feature: Test MQTT API Then In my list there is a collection "kuzzle-collection-test" with 2 room and 2 subscriber When I remove the first room And I get the list subscriptions - Then In my list there is a collection "kuzzle-collection-test" with 1 room and 1 subscriber \ No newline at end of file + Then In my list there is a collection "kuzzle-collection-test" with 1 room and 1 subscriber + + @usingMQTT + Scenario: create additional index + When I create an index named "my-new-index" + Then I'm able to find the index named "my-new-index" in index list + Then I'm not able to find the index named "my-undefined-index" in index list + Then I'm able to delete the index named "my-new-index" \ No newline at end of file diff --git a/features/REST.feature b/features/REST.feature index 114125a177..e7f7a15baa 100644 --- a/features/REST.feature +++ b/features/REST.feature @@ -8,6 +8,7 @@ Feature: Test REST API When I publish a message Then I should receive a request id Then I'm not able to get the document + And I'm not able to get the document in index "index-test-alt" @usingREST Scenario: Create a new document and get it @@ -37,6 +38,7 @@ Feature: Test REST API Scenario: Search a document When I write the document "documentGrace" Then I find a document with "grace" in field "firstName" + And I don't find a document with "grace" in field "firstName" in index "index-test-alt" @usingREST Scenario: Bulk import @@ -61,11 +63,12 @@ Feature: Test REST API When I write the document "documentGrace" When I write the document "documentAda" Then I count 4 documents + And I count 0 documents in index "index-test-alt" And I count 2 documents with "NYC" in field "city" Then I truncate the collection And I count 0 documents - @usingREST @removeSchema + @usingREST Scenario: Change mapping When I write the document "documentGrace" Then I don't find a document with "Grace" in field "firstName" @@ -100,3 +103,10 @@ Feature: Test REST API Scenario: get the Kuzzle timestamp When I get the server timestamp Then I can read the timestamp + + @usingREST + Scenario: create additional index + When I create an index named "my-new-index" + Then I'm able to find the index named "my-new-index" in index list + Then I'm not able to find the index named "my-undefined-index" in index list + Then I'm able to delete the index named "my-new-index" diff --git a/features/STOMP.feature b/features/STOMP.feature index 0c9afc7850..33c75e9dc4 100644 --- a/features/STOMP.feature +++ b/features/STOMP.feature @@ -14,6 +14,7 @@ Feature: Test STOMP API When I write the document Then I should receive a document id Then I'm able to get the document + And I'm not able to get the document in index "index-test-alt" @usingSTOMP @unsubscribe Scenario: Create or Update a document @@ -39,7 +40,8 @@ Feature: Test STOMP API @usingSTOMP Scenario: Search a document When I write the document "documentGrace" - Then I find a document with "grace" in field "firstName" + And I find a document with "grace" in field "firstName" + And I don't find a document with "grace" in field "firstName" in index "index-test-alt" @usingSTOMP Scenario: Bulk import @@ -64,6 +66,7 @@ Feature: Test STOMP API When I write the document "documentGrace" When I write the document "documentAda" Then I count 4 documents + And I count 0 documents in index "index-test-alt" And I count 2 documents with "NYC" in field "city" Then I truncate the collection And I count 0 documents @@ -196,4 +199,11 @@ Feature: Test STOMP API Then In my list there is a collection "kuzzle-collection-test" with 2 room and 2 subscriber When I remove the first room And I get the list subscriptions - Then In my list there is a collection "kuzzle-collection-test" with 1 room and 1 subscriber \ No newline at end of file + Then In my list there is a collection "kuzzle-collection-test" with 1 room and 1 subscriber + + @usingSTOMP + Scenario: create additional index + When I create an index named "my-new-index" + Then I'm able to find the index named "my-new-index" in index list + Then I'm not able to find the index named "my-undefined-index" in index list + Then I'm able to delete the index named "my-new-index" \ No newline at end of file diff --git a/features/Websocket.feature b/features/Websocket.feature index 1eddc929e3..4428994f96 100644 --- a/features/Websocket.feature +++ b/features/Websocket.feature @@ -14,6 +14,7 @@ Feature: Test websocket API When I write the document Then I should receive a document id Then I'm able to get the document + And I'm not able to get the document in index "index-test-alt" @usingWebsocket @unsubscribe Scenario: Create or Update a document @@ -39,7 +40,9 @@ Feature: Test websocket API @usingWebsocket Scenario: Search a document When I write the document "documentGrace" + And I wait 1s Then I find a document with "grace" in field "firstName" + And I don't find a document with "grace" in field "firstName" in index "index-test-alt" @usingWebsocket Scenario: Bulk import @@ -64,11 +67,12 @@ Feature: Test websocket API When I write the document "documentGrace" When I write the document "documentAda" Then I count 4 documents + And I count 0 documents in index "index-test-alt" And I count 2 documents with "NYC" in field "city" Then I truncate the collection And I count 0 documents - @removeSchema @usingWebsocket + @usingWebsocket Scenario: Change mapping When I write the document "documentGrace" Then I don't find a document with "Grace" in field "firstName" @@ -76,6 +80,7 @@ Feature: Test websocket API Then I wait 1s Then I change the schema When I write the document "documentGrace" + And I wait 1s Then I find a document with "Grace" in field "firstName" @usingWebsocket @unsubscribe @@ -196,4 +201,11 @@ Feature: Test websocket API Then In my list there is a collection "kuzzle-collection-test" with 2 room and 2 subscriber When I remove the first room And I get the list subscriptions - Then In my list there is a collection "kuzzle-collection-test" with 1 room and 1 subscriber \ No newline at end of file + Then In my list there is a collection "kuzzle-collection-test" with 1 room and 1 subscriber + + @usingWebsocket + Scenario: create additional index + When I create an index named "my-new-index" + Then I'm able to find the index named "my-new-index" in index list + Then I'm not able to find the index named "my-undefined-index" in index list + Then I'm able to delete the index named "my-new-index" \ No newline at end of file diff --git a/features/step_definitions/api.js b/features/step_definitions/api.js index 02ddbc094a..f6063e5c2e 100644 --- a/features/step_definitions/api.js +++ b/features/step_definitions/api.js @@ -112,51 +112,49 @@ var apiSteps = function () { }); /** READ **/ - this.Then(/^I'm ?(not)* able to get the document$/, function (not, callback) { + this.Then(/^I'm ?(not)* able to get the document(?: in index "([^"]*)")?$/, function (not, index, callback) { var main = function (callbackAsync) { - setTimeout(function () { - this.api.get(this.result._id) - .then(function (body) { - if (body.error && !not) { - if (body.error.message) { - callbackAsync(body.error.message); - return false; - } - - callbackAsync(body.error); + this.api.get(this.result._id, index) + .then(body => { + if (body.error && !not) { + if (body.error.message) { + callbackAsync(body.error.message); return false; } - if (!body.result || !body.result._source) { - if (not) { - callbackAsync(); - return false; - } - - callbackAsync('No result provided'); - return false; - } + callbackAsync(body.error); + return false; + } + if (!body.result || !body.result._source) { if (not) { - callbackAsync('Object with id '+ this.result._id + ' exists'); + callbackAsync(); return false; } + callbackAsync('No result provided'); + return false; + } + + if (not) { + callbackAsync('Object with id '+ this.result._id + ' exists'); + return false; + } + + callbackAsync(); + }) + .catch(function (error) { + if (not) { callbackAsync(); - }.bind(this)) - .catch(function (error) { - if (not) { - callbackAsync(); - return false; - } + return false; + } - callbackAsync(error); - }); - }.bind(this), 20); // end setTimeout + callbackAsync(error); + }); }; - async.retry(20, main.bind(this), function (err) { + async.retry({times: 20, interval: 20}, main.bind(this), function (err) { if (err) { if (err.message) { err = err.message; @@ -164,12 +162,10 @@ var apiSteps = function () { callback(new Error(err)); return false; } - callback(); }); }); - this.Then(/^my document has the value "([^"]*)" in field "([^"]*)"$/, function (value, field, callback) { var main = function (callbackAsync) { setTimeout(function () { @@ -213,14 +209,13 @@ var apiSteps = function () { }); }); - - this.Then(/^I ?(don't)* find a document with "([^"]*)" in field "([^"]*)"$/, function (dont, value, field, callback) { + this.Then(/^I ?(don't)* find a document with "([^"]*)"(?: in field "([^"]*)")?(?: in index "([^"]*)")?$/, function (dont, value, field, index, callback) { var main = function (callbackAsync) { setTimeout(function () { var filters = {filter: {term: {}}}; filters.filter.term[field] = value; - this.api.search(filters) + this.api.search(filters, index) .then(function (body) { if (body.error !== null) { if (dont) { @@ -274,7 +269,6 @@ var apiSteps = function () { }); }); - this.Then(/^I can retrieve actions from bulk import$/, function (callback) { var main = function (callbackAsync) { setTimeout(function () { @@ -344,10 +338,10 @@ var apiSteps = function () { }); }); - this.Then(/^I count ([\d]*) documents$/, function (number, callback) { + this.Then(/^I count ([\d]*) documents(?: in index "([^"]*)")?$/, function (number, index, callback) { var main = function (callbackAsync) { setTimeout(function () { - this.api.count({}) + this.api.count({}, index) .then(function (body) { if (body.error) { callbackAsync(body.error.message); @@ -381,7 +375,7 @@ var apiSteps = function () { }); }); - this.Then(/^I count ([\d]*) documents with "([^"]*)" in field "([^"]*)"/, function (number, value, field, callback) { + this.Then(/^I count ([\d]*) documents with "([^"]*)" in field "([^"]*)(?: in index "([^"]*)")?"/, function (number, value, field, index, callback) { var main = function (callbackAsync) { setTimeout(function () { var filter = { @@ -392,7 +386,7 @@ var apiSteps = function () { filter.query.match[field] = value; - this.api.count(filter) + this.api.count(filter, index) .then(function (body) { if (body.error) { callbackAsync(body.error.message); @@ -514,8 +508,8 @@ var apiSteps = function () { }); }); - this.When(/^I list data collections$/, function (callback) { - this.api.listCollections() + this.When(/^I list data collections(?: in index "([^"]*)")?$/, function (index, callback) { + this.api.listCollections(index) .then(response => { if (response.error) { callback(new Error(response.error.message)); @@ -672,10 +666,10 @@ var apiSteps = function () { }); }); - this.When(/^I write the document ?(?:"([^"]*)")?$/, function (documentName, callback) { + this.When(/^I write the document ?(?:"([^"]*)")?(?: in index "([^"]*)")?$/, function (documentName, index, callback) { var document = this[documentName] || this.documentGrace; - this.api.create(document) + this.api.create(document, index) .then(function (body) { if (body.error) { callback(new Error(body.error.message)); @@ -750,13 +744,13 @@ var apiSteps = function () { 'Received document: ' + JSON.stringify(this.updatedResult))); }); - this.Then(/^I update the document with value "([^"]*)" in field "([^"]*)"$/, function (value, field, callback) { + this.Then(/^I update the document with value "([^"]*)" in field "([^"]*)"(?: in index "([^"]*)")?$/, function (value, field, index, callback) { var main = function (callbackAsync) { setTimeout(function () { var body = {}; body[field] = value; - this.api.update(this.result._id, body) + this.api.update(this.result._id, body, index) .then(function (body) { if (body.error) { callbackAsync(body.error.message); @@ -786,9 +780,8 @@ var apiSteps = function () { }); }); - - this.Then(/^I remove the document$/, function (callback) { - this.api.deleteById(this.result._id) + this.Then(/^I remove the document(?: in index "([^"]*)")?$/, function (index, callback) { + this.api.deleteById(this.result._id, index) .then(function (body) { if (body.error !== null) { callback(body.error.message); @@ -802,14 +795,14 @@ var apiSteps = function () { }); }); - this.Then(/^I remove documents with field "([^"]*)" equals to value "([^"]*)"$/, function (field, value, callback) { + this.Then(/^I remove documents with field "([^"]*)" equals to value "([^"]*)"(?: in index "([^"]*)")?$/, function (field, value, index, callback) { var main = function (callbackAsync) { setTimeout(function () { var filter = { query: { match: {} } }; filter.query.match[field] = value; - this.api.deleteByQuery(filter) + this.api.deleteByQuery(filter, index) .then(function (body) { if (body.error) { callbackAsync(body.error.message); @@ -839,9 +832,8 @@ var apiSteps = function () { }); }); - - this.When(/^I do a bulk import$/, function (callback) { - this.api.bulkImport(this.bulk) + this.When(/^I do a bulk import(?: from index "([^"]*)")?$/, function (index, callback) { + this.api.bulkImport(this.bulk, index) .then(function (body) { if (body.error !== null) { callback(new Error(body.error.message)); @@ -870,8 +862,8 @@ var apiSteps = function () { }); }); - this.Then(/^I remove the collection and schema$/, function (callback) { - this.api.deleteCollection() + this.Then(/^I remove the collection and schema(?: from index "([^"]*)")?$/, function (index, callback) { + this.api.deleteCollection(index) .then(function (body) { if (body.error !== null) { return callback(new Error(body.error.message)); @@ -884,8 +876,7 @@ var apiSteps = function () { }); }); - - this.Then(/^I change the schema$/, function (callback) { + this.Then(/^I change the schema(?: in index "([^"]*)")?$/, function (index, callback) { this.api.putMapping() .then(function (body) { if (body.error !== null) { @@ -900,8 +891,8 @@ var apiSteps = function () { }); }); - this.Then(/^I truncate the collection$/, function (callback) { - this.api.truncateCollection() + this.Then(/^I truncate the collection(?: in index "([^"]*)")?$/, function (index, callback) { + this.api.truncateCollection(index) .then(body => { if (body.error !== null) { return callback(new Error(body.error.message)); @@ -935,12 +926,17 @@ var apiSteps = function () { }); }); - this.Then(/^In my list there is a collection "([^"]*)" with ([\d]*) room and ([\d]*) subscriber$/, function (collection, countRooms, countSubscribers, callback) { - if (!this.result[collection]) { + this.Then(/^In my list there is a collection "([^"]*)" with ([\d]*) room and ([\d]*) subscriber$/, function(collection, countRooms, countSubscribers, callback) { + + if (!this.result[this.fakeIndex]) { + return callback(new Error('No entry for index ' + this.fakeIndex)); + } + + if (!this.result[this.fakeIndex][collection]) { return callback(new Error('No entry for collection ' + collection)); } - var rooms = Object.keys(this.result[collection]); + var rooms = Object.keys(this.result[this.fakeIndex][collection]); if (rooms.length !== parseInt(countRooms)) { return callback(new Error('Wrong number rooms for collection ' + collection + '. Expected ' + countRooms + ' get ' + rooms.length)); @@ -949,7 +945,7 @@ var apiSteps = function () { var count = 0; rooms.forEach(roomId => { - count += this.result[collection][roomId]; + count += this.result[this.fakeIndex][collection][roomId]; }); if (count !== parseInt(countSubscribers)) { @@ -959,6 +955,110 @@ var apiSteps = function () { callback(); }); + this.When(/^I create an index named "([^"]*)"$/, function (index, callback) { + this.api.createIndex(index) + .then(body => { + if (body.error) { + callback(new Error(body.error.message)); + return false; + } + + if (!body.result) { + callback(new Error('No result provided')); + return false; + } + + this.result = body.result; + callback(); + }) + .catch(error => callback(error)); + }); + + this.Then(/^I'm ?(not)* able to find the index named "([^"]*)" in index list$/, function (not, index, callback) { + var main = function (callbackAsync) { + this.api.listIndexes() + .then(body => { + if (body.error && !not) { + if (body.error.message) { + callbackAsync(body.error.message); + return false; + } + + callbackAsync(body.error); + return false; + } + + if (!body.result || !body.result.indexes) { + if (not) { + callbackAsync(); + return true; + } + + callbackAsync('No result provided'); + return false; + } + + if (body.result.indexes.indexOf(index) !== -1) { + if (not) { + callbackAsync('Index ' + index + ' exists'); + return false; + } + + callbackAsync(); + return true; + } + + + if (not) { + callbackAsync(); + return true; + } + + callbackAsync('Index ' + index + ' is missing'); + }) + .catch(function (error) { + if (not) { + callbackAsync(); + return false; + } + + callbackAsync(error); + return true; + }); + }; + + + async.retry({times: 20, interval: 20}, main.bind(this), function (err) { + if (err) { + if (err.message) { + err = err.message; + } + callback(new Error(err)); + return false; + } + callback(); + }); + }); + + this.Then(/^I'm able to delete the index named "([^"]*)"$/, function (index, callback) { + + this.api.deleteIndex(index) + .then(body => { + if (body.error) { + if (body.error.message) { + callback(body.error.message); + return false; + } + + callback(body.error); + return false; + } + + callback(); + }) + .catch(error => callback(error)); + }); + /** TOOLS **/ this.Then(/^I wait ([\d]*)s$/, function (time, callback) { setTimeout(function () { diff --git a/features/support/apiMQTT.js b/features/support/apiMQTT.js index 1503aa91fe..7431b6bba5 100644 --- a/features/support/apiMQTT.js +++ b/features/support/apiMQTT.js @@ -41,6 +41,7 @@ ApiMQTT.prototype.unsubscribe = function (room, clientId) { clientId: clientId, controller: 'subscribe', collection: this.world.fakeCollection, + index: this.world.fakeIndex, action: 'off', body: { roomId: room } }; diff --git a/features/support/apiREST.js b/features/support/apiREST.js index b1dd04a82c..e083e89566 100644 --- a/features/support/apiREST.js +++ b/features/support/apiREST.js @@ -13,16 +13,16 @@ ApiREST.prototype.init = function (world) { ApiREST.prototype.disconnect = function () {}; ApiREST.prototype.pathApi = function (path) { - return config.url + '/api/' + path; + return config.url + '/api/v1.0/' + path; }; ApiREST.prototype.callApi = function (options) { return rp(options); }; -ApiREST.prototype.get = function (id) { +ApiREST.prototype.get = function (id, index) { var options = { - url: this.pathApi(this.world.fakeCollection + '/' + id), + url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/' + id), method: 'GET', json: true }; @@ -30,9 +30,9 @@ ApiREST.prototype.get = function (id) { return this.callApi(options); }; -ApiREST.prototype.search = function (filters) { +ApiREST.prototype.search = function (filters, index) { var options = { - url: this.pathApi(this.world.fakeCollection + '/_search'), + url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_search'), method: 'POST', json: filters }; @@ -40,9 +40,9 @@ ApiREST.prototype.search = function (filters) { return this.callApi(options); }; -ApiREST.prototype.count = function (filters) { +ApiREST.prototype.count = function (filters, index) { var options = { - url: this.pathApi(this.world.fakeCollection + '/_count'), + url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_count'), method: 'POST', json: filters }; @@ -50,9 +50,9 @@ ApiREST.prototype.count = function (filters) { return this.callApi(options); }; -ApiREST.prototype.create = function (body) { +ApiREST.prototype.create = function (body, index) { var options = { - url: this.pathApi(this.world.fakeCollection) + '/_create', + url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_create'), method: 'POST', json: body }; @@ -60,9 +60,9 @@ ApiREST.prototype.create = function (body) { return this.callApi(options); }; -ApiREST.prototype.publish = function (body) { +ApiREST.prototype.publish = function (body, index) { var options = { - url: this.pathApi(this.world.fakeCollection), + url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection), method: 'POST', json: body }; @@ -70,9 +70,9 @@ ApiREST.prototype.publish = function (body) { return this.callApi(options); }; -ApiREST.prototype.createOrUpdate = function (body) { +ApiREST.prototype.createOrUpdate = function (body, index) { var options = { - url: this.pathApi(this.world.fakeCollection + '/' + body._id), + url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/' + body._id), method: 'PUT', json: body }; @@ -80,9 +80,9 @@ ApiREST.prototype.createOrUpdate = function (body) { return this.callApi(options); }; -ApiREST.prototype.update = function (id, body) { +ApiREST.prototype.update = function (id, body, index) { var options = { - url: this.pathApi(this.world.fakeCollection + '/' + id + '/_update'), + url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/' + id + '/_update'), method: 'PUT', json: body }; @@ -90,9 +90,9 @@ ApiREST.prototype.update = function (id, body) { return this.callApi(options); }; -ApiREST.prototype.deleteById = function (id) { +ApiREST.prototype.deleteById = function (id, index) { var options = { - url: this.pathApi(this.world.fakeCollection + '/' + id), + url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/' + id), method: 'DELETE', json: true }; @@ -100,9 +100,9 @@ ApiREST.prototype.deleteById = function (id) { return this.callApi(options); }; -ApiREST.prototype.deleteByQuery = function (filters) { +ApiREST.prototype.deleteByQuery = function (filters, index) { var options = { - url: this.pathApi(this.world.fakeCollection + '/_query'), + url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_query'), method: 'DELETE', json: filters }; @@ -110,9 +110,9 @@ ApiREST.prototype.deleteByQuery = function (filters) { return this.callApi(options); }; -ApiREST.prototype.deleteCollection = function () { +ApiREST.prototype.deleteCollection = function (index) { var options = { - url: this.pathApi(this.world.fakeCollection), + url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection), method: 'DELETE', json: true }; @@ -120,9 +120,9 @@ ApiREST.prototype.deleteCollection = function () { return this.callApi(options); }; -ApiREST.prototype.bulkImport = function (bulk) { +ApiREST.prototype.bulkImport = function (bulk, index) { var options = { - url: this.pathApi(this.world.fakeCollection + '/_bulk'), + url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_bulk'), method: 'POST', json: bulk }; @@ -132,7 +132,7 @@ ApiREST.prototype.bulkImport = function (bulk) { ApiREST.prototype.globalBulkImport = function (bulk) { var options = { - url: this.pathApi('/_bulk'), + url: this.pathApi('_bulk'), method: 'POST', json: bulk }; @@ -140,9 +140,9 @@ ApiREST.prototype.globalBulkImport = function (bulk) { return this.callApi(options); }; -ApiREST.prototype.putMapping = function () { +ApiREST.prototype.putMapping = function (index) { var options = { - url: this.pathApi(this.world.fakeCollection + '/_mapping'), + url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_mapping'), method: 'PUT', json: this.world.schema }; @@ -152,7 +152,7 @@ ApiREST.prototype.putMapping = function () { ApiREST.prototype.getStats = function (dates) { var options = { - url: this.pathApi('/_getStats'), + url: this.pathApi('_getStats'), method: 'POST', json: dates }; @@ -162,7 +162,7 @@ ApiREST.prototype.getStats = function (dates) { ApiREST.prototype.getLastStats = function () { var options = { - url: this.pathApi('/_getLastStats'), + url: this.pathApi('_getLastStats'), method: 'GET', json: {} }; @@ -172,7 +172,7 @@ ApiREST.prototype.getLastStats = function () { ApiREST.prototype.getAllStats = function () { var options = { - url: this.pathApi('/_getAllStats'), + url: this.pathApi('_getAllStats'), method: 'GET', json: {} }; @@ -180,9 +180,13 @@ ApiREST.prototype.getAllStats = function () { return this.callApi(options); }; -ApiREST.prototype.listCollections = function () { - var options = { - url: this.pathApi('_listCollections'), +ApiREST.prototype.listCollections = function (index) { + var options; + + index = index || this.world.fakeIndex; + + options = { + url: this.pathApi(index + '/_listCollections'), method: 'GET', json: true }; @@ -200,9 +204,49 @@ ApiREST.prototype.now = function () { return this.callApi(options); }; -ApiREST.prototype.truncateCollection = function () { +ApiREST.prototype.truncateCollection = function (index) { + var options = { + url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_truncate'), + method: 'DELETE', + json: true + }; + + return this.callApi(options); +}; + +ApiREST.prototype.listIndexes = function () { + var options = { + url: this.pathApi('_listIndexes'), + method: 'GET', + json: true + }; + + return this.callApi(options); +}; + +ApiREST.prototype.deleteIndexes = function () { + var options = { + url: this.pathApi('_deleteIndexes'), + method: 'DELETE', + json: true + }; + + return this.callApi(options); +}; + +ApiREST.prototype.createIndex = function (index) { + var options = { + url: this.pathApi(index), + method: 'PUT', + json: true + }; + + return this.callApi(options); +}; + +ApiREST.prototype.deleteIndex = function (index) { var options = { - url: this.pathApi(this.world.fakeCollection + '/_truncate'), + url: this.pathApi(index), method: 'DELETE', json: true }; diff --git a/features/support/apiRT.js b/features/support/apiRT.js index 7e87ab563a..267a7f0cda 100644 --- a/features/support/apiRT.js +++ b/features/support/apiRT.js @@ -15,11 +15,12 @@ var ApiRT = function () { ApiRT.prototype.send = function () {}; ApiRT.prototype.sendAndListen = function () {}; -ApiRT.prototype.create = function (body) { +ApiRT.prototype.create = function (body, index) { var msg = { controller: 'write', collection: this.world.fakeCollection, + index: index || this.world.fakeIndex, action: 'create', body: body }; @@ -27,11 +28,12 @@ ApiRT.prototype.create = function (body) { return this.send(msg); }; -ApiRT.prototype.publish = function (body) { +ApiRT.prototype.publish = function (body, index) { var msg = { controller: 'write', collection: this.world.fakeCollection, + index: index || this.world.fakeIndex, action: 'publish', body: body }; @@ -39,11 +41,12 @@ ApiRT.prototype.publish = function (body) { return this.send(msg); }; -ApiRT.prototype.createOrUpdate = function (body) { +ApiRT.prototype.createOrUpdate = function (body, index) { var msg = { controller: 'write', collection: this.world.fakeCollection, + index: index || this.world.fakeIndex, action: 'createOrUpdate', body: body }; @@ -51,11 +54,12 @@ ApiRT.prototype.createOrUpdate = function (body) { return this.send(msg); }; -ApiRT.prototype.get = function (id) { +ApiRT.prototype.get = function (id, index) { var msg = { controller: 'read', collection: this.world.fakeCollection, + index: index || this.world.fakeIndex, action: 'get', _id: id }; @@ -63,11 +67,12 @@ ApiRT.prototype.get = function (id) { return this.send(msg); }; -ApiRT.prototype.search = function (filters) { +ApiRT.prototype.search = function (filters, index) { var msg = { controller: 'read', collection: this.world.fakeCollection, + index: index || this.world.fakeIndex, action: 'search', body: filters }; @@ -75,11 +80,12 @@ ApiRT.prototype.search = function (filters) { return this.send(msg); }; -ApiRT.prototype.count = function (filters) { +ApiRT.prototype.count = function (filters, index) { var msg = { controller: 'read', collection: this.world.fakeCollection, + index: index || this.world.fakeIndex, action: 'count', body: filters }; @@ -87,11 +93,12 @@ ApiRT.prototype.count = function (filters) { return this.send(msg); }; -ApiRT.prototype.update = function (id, body) { +ApiRT.prototype.update = function (id, body, index) { var msg = { controller: 'write', collection: this.world.fakeCollection, + index: index || this.world.fakeIndex, action: 'update', _id: id, body: body @@ -100,11 +107,12 @@ ApiRT.prototype.update = function (id, body) { return this.send(msg); }; -ApiRT.prototype.deleteById = function (id) { +ApiRT.prototype.deleteById = function (id, index) { var msg = { controller: 'write', collection: this.world.fakeCollection, + index: index || this.world.fakeIndex, action: 'delete', _id: id }; @@ -112,11 +120,12 @@ ApiRT.prototype.deleteById = function (id) { return this.send(msg); }; -ApiRT.prototype.deleteByQuery = function (filters) { +ApiRT.prototype.deleteByQuery = function (filters, index) { var msg = { controller: 'write', collection: this.world.fakeCollection, + index: index || this.world.fakeIndex, action: 'deleteByQuery', body: filters }; @@ -124,22 +133,24 @@ ApiRT.prototype.deleteByQuery = function (filters) { return this.send(msg); }; -ApiRT.prototype.deleteCollection = function () { +ApiRT.prototype.deleteCollection = function (index) { var msg = { controller: 'admin', collection: this.world.fakeCollection, + index: index || this.world.fakeIndex, action: 'deleteCollection' }; return this.send(msg); }; -ApiRT.prototype.putMapping = function () { +ApiRT.prototype.putMapping = function (index) { var msg = { controller: 'admin', collection: this.world.fakeCollection, + index: index || this.world.fakeIndex, action: 'putMapping', body: this.world.schema }; @@ -147,11 +158,12 @@ ApiRT.prototype.putMapping = function () { return this.send(msg); }; -ApiRT.prototype.bulkImport = function (bulk) { +ApiRT.prototype.bulkImport = function (bulk, index) { var msg = { controller: 'bulk', collection: this.world.fakeCollection, + index: index || this.world.fakeIndex, action: 'import', body: bulk }; @@ -175,6 +187,7 @@ ApiRT.prototype.subscribe = function (filters, client) { msg = { controller: 'subscribe', collection: this.world.fakeCollection, + index: this.world.fakeIndex, action: 'on', users: 'all', body: null @@ -193,6 +206,7 @@ ApiRT.prototype.unsubscribe = function (room, clientId) { clientId: clientId, controller: 'subscribe', collection: this.world.fakeCollection, + index: this.world.fakeIndex, action: 'off', body: { roomId: room } }; @@ -210,6 +224,7 @@ ApiRT.prototype.countSubscription = function () { msg = { controller: 'subscribe', collection: this.world.fakeCollection, + index: this.world.fakeIndex, action: 'count', body: { roomId: rooms[0] @@ -250,10 +265,11 @@ ApiRT.prototype.getAllStats = function () { return this.send(msg); }; -ApiRT.prototype.listCollections = function () { +ApiRT.prototype.listCollections = function (index) { var msg = { controller: 'read', + index: index || this.world.fakeIndex, action: 'listCollections' }; @@ -270,11 +286,12 @@ ApiRT.prototype.now = function () { return this.send(msg); }; -ApiRT.prototype.truncateCollection = function () { +ApiRT.prototype.truncateCollection = function (index) { var msg = { controller: 'admin', collection: this.world.fakeCollection, + index: index || this.world.fakeIndex, action: 'truncateCollection' }; @@ -291,12 +308,55 @@ ApiRT.prototype.listSubscriptions = function () { return this.send(msg); }; -ApiRT.prototype.removeRooms = function (rooms) { +ApiRT.prototype.deleteIndexes = function () { + var + msg = { + controller: 'admin', + action: 'deleteIndexes' + }; + + return this.send(msg); +}; + +ApiRT.prototype.listIndexes = function () { + var + msg = { + controller: 'read', + action: 'listIndexes' + }; + + return this.send(msg); +}; + +ApiRT.prototype.createIndex = function (index) { + var + msg = { + controller: 'admin', + action: 'createIndex', + index: index + }; + + return this.send(msg); +}; + +ApiRT.prototype.deleteIndex = function (index) { + var + msg = { + controller: 'admin', + action: 'deleteIndex', + index: index + }; + + return this.send(msg); +}; + +ApiRT.prototype.removeRooms = function (rooms, index) { var msg = { controller: 'admin', action: 'removeRooms', collection: this.world.fakeCollection, + index: index || this.world.fakeIndex, body: {rooms: rooms} }; diff --git a/features/support/apiSTOMP.js b/features/support/apiSTOMP.js index 3b8325a05e..c1e170d708 100644 --- a/features/support/apiSTOMP.js +++ b/features/support/apiSTOMP.js @@ -54,6 +54,7 @@ ApiSTOMP.prototype.unsubscribe = function (room, clientId) { clientId: clientId, controller: 'subscribe', collection: this.world.fakeCollection, + index: this.world.fakeIndex, action: 'off', body: { roomId: room } }; diff --git a/features/support/apiWebsocket.js b/features/support/apiWebsocket.js index 9c8573b80f..f82ffef184 100644 --- a/features/support/apiWebsocket.js +++ b/features/support/apiWebsocket.js @@ -34,6 +34,7 @@ ApiWebsocket.prototype.unsubscribe = function (room, socketName) { controller: 'subscribe', action: 'off', collection: this.world.fakeCollection, + index: this.world.fakeIndex, body: { roomId: room } }; @@ -90,9 +91,7 @@ ApiWebsocket.prototype.sendAndListen = function (msg, socketName) { socketName = initSocket.call(this, socketName); this.listSockets[socketName].once(msg.requestId, response => { - var listener = function (document) { - this.responses = document; - }; + var listener = document => this.responses = document; if (response.error) { deferred.reject(response.error.message); diff --git a/features/support/hooks.js b/features/support/hooks.js index d55b2a2908..29130d3ab0 100644 --- a/features/support/hooks.js +++ b/features/support/hooks.js @@ -43,25 +43,37 @@ var myHooks = function () { callback(); }); - this.After(function (scenario, callback) { - this.api.deleteByQuery({}) - .then(function () { - this.api.disconnect(); + this.registerHandler('BeforeFeature', (event, callback) => { + this.api = setAPI(this, 'Websocket'); + this.api.createIndex((new this.World()).fakeIndex) + .then(this.api.createIndex((new this.World()).fakeAltIndex)) + .then(() => { + setTimeout(callback, 1000); + }) + .catch(error => callback(new Error(error))); + }); + + this.registerHandler('AfterFeature', function (event, callback) { + this.api = setAPI(this, 'Websocket'); + this.api.deleteIndexes() + .then(function () { callback(); - }.bind(this)) + }) .catch(function (error) { callback(new Error(error)); }); }); - this.After('@removeSchema', function (scenario, callback) { + this.After(function (scenario, callback) { this.api.deleteCollection() .then(function () { - setTimeout(callback, 1000); - }) - .catch(function (error) { - callback(new Error(error)); + this.api.disconnect(); + + callback(); + }.bind(this)) + .catch(function () { + callback(); }); }); diff --git a/features/support/world.js b/features/support/world.js index 4d6ff1e8af..925f1c467b 100644 --- a/features/support/world.js +++ b/features/support/world.js @@ -3,6 +3,8 @@ module.exports = function () { this.api = null; // Fake values for test + this.fakeIndex = 'index-test'; + this.fakeAltIndex = 'index-test-alt'; this.fakeCollection = 'kuzzle-collection-test'; this.documentGrace = { @@ -37,13 +39,13 @@ module.exports = function () { { delete: {_id: 2 } } ]; this.globalBulk = [ - { index: {_id: 1, _type: 'kuzzle-collection-test' } }, + { index: {_id: 1, _type: this.fakeCollection, _index: this.fakeIndex } }, { title: 'foo' }, - { index: {_id: 2, _type: 'kuzzle-collection-test' } }, + { index: {_id: 2, _type: this.fakeCollection, _index: this.fakeIndex } }, { title: 'bar' }, - { update: {_id: 1, _type: 'kuzzle-collection-test' } }, + { update: {_id: 1, _type: this.fakeCollection, _index: this.fakeIndex } }, { doc: { title: 'foobar' } }, - { delete: {_id: 2, _type: 'kuzzle-collection-test' } } + { delete: {_id: 2, _type: this.fakeCollection, _index: this.fakeIndex } } ]; this.schema = { diff --git a/lib/api/Kuzzle.js b/lib/api/Kuzzle.js index be1758ea25..5c56d83d41 100644 --- a/lib/api/Kuzzle.js +++ b/lib/api/Kuzzle.js @@ -29,6 +29,8 @@ function Kuzzle () { this.workers = new Workers(this); this.services = new Services(this); + this.indexes = {}; + // Add methods this.cleanDb = require('./cleanDb'); this.prepareDb = require('./prepareDb'); diff --git a/lib/api/cleanDb.js b/lib/api/cleanDb.js index ae9fd225a6..1d84d7c660 100644 --- a/lib/api/cleanDb.js +++ b/lib/api/cleanDb.js @@ -1,4 +1,6 @@ -var q = require('q'); +var + RequestObject = require('./core/models/requestObject'), + q = require('q'); module.exports = function () { @@ -10,7 +12,10 @@ module.exports = function () { return deferred.promise; } - this.services.list.writeEngine.reset() + this.services.list.writeEngine.deleteIndexes(new RequestObject({})) + // @todo : manage internal index properly + // create internal index + .then(this.services.list.writeEngine.createIndex(new RequestObject({index: '%kuzzle'}))) .then(function () { this.pluginsManager.trigger('cleanDb:done', 'Reset done: Kuzzle is now like a virgin, touched for the very first time !'); diff --git a/lib/api/controllers/adminController.js b/lib/api/controllers/adminController.js index cc5c3fa453..644b2ece68 100644 --- a/lib/api/controllers/adminController.js +++ b/lib/api/controllers/adminController.js @@ -1,34 +1,27 @@ var - q = require('q'), ResponseObject = require('../core/models/responseObject'); module.exports = function AdminController (kuzzle) { /** * Delete the entire collection and associate mapping * @param {RequestObject} requestObject - * @returns {*} + * @returns {Promise} */ this.deleteCollection = function (requestObject) { - var deferred = q.defer(); - kuzzle.pluginsManager.trigger('data:deleteCollection', requestObject); - deferred.resolve({}); - return deferred.promise; + return Promise.resolve({}); }; /** * Add a mapping to the collection * @param {RequestObject} requestObject - * @returns {*} + * @returns {Promise} */ this.putMapping = function (requestObject) { - var deferred = q.defer(); - kuzzle.pluginsManager.trigger('data:putMapping', requestObject); - deferred.resolve({}); - return deferred.promise; + return Promise.resolve({}); }; /** @@ -78,15 +71,12 @@ module.exports = function AdminController (kuzzle) { * Reset a collection by removing all documents while keeping the existing mapping. * * @param {RequestObject} requestObject - * @returns {promise} + * @returns {Promise} */ this.truncateCollection = function (requestObject) { - var deferred = q.defer(); - kuzzle.pluginsManager.trigger('data:truncateCollection', requestObject); - deferred.resolve({}); - return deferred.promise; + return Promise.resolve({}); }; /** @@ -100,9 +90,43 @@ module.exports = function AdminController (kuzzle) { return kuzzle.repositories.role.validateAndSaveRole( kuzzle.repositories.role.getRoleFromRequestObject(requestObject) ) - .then(result => { - return Promise.resolve(new ResponseObject(requestObject, result)); - }); + .then(result => { + return Promise.resolve(new ResponseObject(requestObject, result)); + }); + }; + + /** + * Reset all indexes + * + * @param {RequestObject} requestObject + * @returns {Promise} + */ + this.deleteIndexes = function (requestObject) { + kuzzle.pluginsManager.trigger('data:deleteIndexes', requestObject); + + return Promise.resolve({}); + }; + + /** + * Create an empty index + * @param {RequestObject} requestObject + * @returns {Promise} + */ + this.createIndex = function (requestObject) { + kuzzle.pluginsManager.trigger('data:createIndex', requestObject); + + return Promise.resolve({}); + }; + + /** + * Delete the entire index and associated collections + * @param {RequestObject} requestObject + * @returns {Promise} + */ + this.deleteIndex = function (requestObject) { + kuzzle.pluginsManager.trigger('data:deleteIndex', requestObject); + + return Promise.resolve({}); }; /** diff --git a/lib/api/controllers/funnelController.js b/lib/api/controllers/funnelController.js index 7f5ac20727..2976724ad6 100644 --- a/lib/api/controllers/funnelController.js +++ b/lib/api/controllers/funnelController.js @@ -40,21 +40,20 @@ module.exports = function FunnelController (kuzzle) { kuzzle.statistics.startRequest(requestObject); requestObject.checkInformation() - .then(function () { - // Test if a controller and an action exist for the object - if (!this[requestObject.controller] - || !this[requestObject.controller][requestObject.action] - || typeof this[requestObject.controller][requestObject.action] !== 'function') { + .then(() => { + if (!this[requestObject.controller] || + !this[requestObject.controller][requestObject.action] || + typeof this[requestObject.controller][requestObject.action] !== 'function') { return Promise.reject(new BadRequestError('No corresponding action ' + requestObject.action + ' in controller ' + requestObject.controller)); } // check if the current user is allowed to process - if (context.user.profile.isActionAllowed(requestObject, context) === false) { - return Promise.reject(new UnauthorizedError('Unauthorized action [' + requestObject.collection + '/' + requestObject.controller + '/' + requestObject.action + '] for user ' + context.user._id, 401)); + if (context.user.profile.isActionAllowed(requestObject, context, kuzzle.indexes) === false) { + return Promise.reject(new UnauthorizedError('Unauthorized action [' + requestObject.index + '/' + requestObject.collection + '/' + requestObject.controller + '/' + requestObject.action + '] for user ' + context.user._id, 401)); } return this[requestObject.controller][requestObject.action](requestObject, context); - }.bind(this)) + }) .then(function (responseObject) { kuzzle.statistics.completedRequest(requestObject); deferred.resolve(responseObject); diff --git a/lib/api/controllers/readController.js b/lib/api/controllers/readController.js index 8401f3bd9b..b6b8ee32a2 100644 --- a/lib/api/controllers/readController.js +++ b/lib/api/controllers/readController.js @@ -1,5 +1,4 @@ var - q = require('q'), ResponseObject = require('../core/models/responseObject'); module.exports = function ReadController (kuzzle) { @@ -29,12 +28,14 @@ module.exports = function ReadController (kuzzle) { }; this.now = function (requestObject) { - var deferred = q.defer(); - kuzzle.pluginsManager.trigger('data:now', requestObject); - deferred.resolve(new ResponseObject(requestObject, {now: Date.now()})); + return Promise.resolve(new ResponseObject(requestObject, {now: Date.now()})); + }; + + this.listIndexes = function (requestObject) { + kuzzle.pluginsManager.trigger('data:listIndexes', requestObject); - return deferred.promise; + return kuzzle.services.list.readEngine.listIndexes(requestObject); }; }; \ No newline at end of file diff --git a/lib/api/controllers/routerController.js b/lib/api/controllers/routerController.js index 02c9437d67..1bdc8460b2 100644 --- a/lib/api/controllers/routerController.js +++ b/lib/api/controllers/routerController.js @@ -26,28 +26,36 @@ module.exports = function RouterController (kuzzle) { routes = [ {verb: 'get', url: '/_listCollections', controller: 'read', action: 'listCollections'}, {verb: 'get', url: '/_getLastStats', controller: 'admin', action: 'getLastStats'}, - {verb: 'post', url: '/_getStats', controller: 'admin', action: 'getStats'}, {verb: 'get', url: '/_getAllStats', controller: 'admin', action: 'getAllStats'}, {verb: 'get', url: '/_now', controller: 'read', action: 'now'}, - {verb: 'put', url: '/_role/:id', controller: 'admin', action: 'putRole'}, + {verb: 'get', url: '/_listIndexes', controller: 'read', action: 'listIndexes'}, {verb: 'get', url: '/_listSubscriptions', controller: 'subscribe', action: 'list'}, - {verb: 'get', url: '/:collection/_mapping', controller: 'admin', action: 'getMapping'}, - {verb: 'get', url: '/:collection/:id', controller: 'read', action: 'get'}, + {verb: 'get', url: '/:index/_listCollections', controller: 'read', action: 'listCollections'}, + {verb: 'get', url: '/:index/:collection/_mapping', controller: 'admin', action: 'getMapping'}, + {verb: 'get', url: '/:index/:collection/:id', controller: 'read', action: 'get'}, + {verb: 'post', url: '/_bulk', controller: 'bulk', action: 'import'}, - {verb: 'post', url: '/:collection/_bulk', controller: 'bulk', action: 'import'}, - {verb: 'post', url: '/:collection/_search', controller: 'read', action: 'search'}, - {verb: 'post', url: '/:collection/_count', controller: 'read', action: 'count'}, - {verb: 'post', url: '/:collection/_create', controller: 'write', action: 'create'}, - {verb: 'post', url: '/:collection', controller: 'write', action: 'publish'}, - {verb: 'delete', url: '/:collection/_query', controller: 'write', action: 'deleteByQuery'}, - {verb: 'delete', url: '/:collection/_truncate', controller: 'admin', action: 'truncateCollection'}, - {verb: 'delete', url: '/:collection/_rooms', controller: 'admin', action: 'removeRooms'}, - {verb: 'delete', url: '/:collection/:id', controller: 'write', action: 'delete'}, - {verb: 'delete', url: '/:collection', controller: 'admin', action: 'deleteCollection'}, - {verb: 'put', url: '/:collection', controller: 'write', action: 'createCollection'}, - {verb: 'put', url: '/:collection/_mapping', controller: 'admin', action: 'putMapping'}, - {verb: 'put', url: '/:collection/:id/_:action', controller: 'write'}, - {verb: 'put', url: '/:collection/:id', controller: 'write', action: 'createOrUpdate'} + {verb: 'post', url: '/_getStats', controller: 'admin', action: 'getStats'}, + {verb: 'post', url: '/:index/_bulk', controller: 'bulk', action: 'import'}, + {verb: 'post', url: '/:index/:collection/_bulk', controller: 'bulk', action: 'import'}, + {verb: 'post', url: '/:index/:collection/_search', controller: 'read', action: 'search'}, + {verb: 'post', url: '/:index/:collection/_count', controller: 'read', action: 'count'}, + {verb: 'post', url: '/:index/:collection/_create', controller: 'write', action: 'create'}, + {verb: 'post', url: '/:index/:collection', controller: 'write', action: 'publish'}, + + {verb: 'delete', url: '/_deleteIndexes', controller: 'admin', action: 'deleteIndexes'}, + {verb: 'delete', url: '/:index', controller: 'admin', action: 'deleteIndex'}, + {verb: 'delete', url: '/:index/:collection/_query', controller: 'write', action: 'deleteByQuery'}, + {verb: 'delete', url: '/:index/:collection/_truncate', controller: 'admin', action: 'truncateCollection'}, + {verb: 'delete', url: '/:index/:collection/:id', controller: 'write', action: 'delete'}, + {verb: 'delete', url: '/:index/:collection', controller: 'admin', action: 'deleteCollection'}, + + {verb: 'put', url: '/_role/:id', controller: 'admin', action: 'putRole'}, + {verb: 'put', url: '/:index', controller: 'admin', action: 'createIndex'}, + {verb: 'put', url: '/:index/:collection', controller: 'write', action: 'createCollection'}, + {verb: 'put', url: '/:index/:collection/_mapping', controller: 'admin', action: 'putMapping'}, + {verb: 'put', url: '/:index/:collection/:id/_:action', controller: 'write'}, + {verb: 'put', url: '/:index/:collection/:id', controller: 'write', action: 'createOrUpdate'} ]; routes = routes.concat(kuzzle.pluginsManager.routes); @@ -60,7 +68,7 @@ module.exports = function RouterController (kuzzle) { } // create and mount a new router for our API - this.router.use('/api/', api); + this.router.use('/api/v1.0', api); // create and mount a new router for plugins this.pluginRouter = new Router(); @@ -120,6 +128,7 @@ module.exports = function RouterController (kuzzle) { }; kuzzle.statistics.newConnection(context.connection); + socket.on(routerCtrl.routename, function (data) { var requestObject; @@ -280,6 +289,12 @@ function executeFromRest(params, request, response) { data._id = request.params.id; delete request.params.id; } + + if (request.params.index) { + data.index = request.params.index; + delete request.params.index; + } + _.forEach(request.params, function (value, param) { request.body[param] = value; }); @@ -304,11 +319,11 @@ function executeFromRest(params, request, response) { } else { /* - the controller did not respond with a valid ResponseObject, - most likely because it deferred its treatment to a worker. - We keep the connection open to listen to the feedback coming - from the worker. - */ + the controller did not respond with a valid ResponseObject, + most likely because it deferred its treatment to a worker. + We keep the connection open to listen to the feedback coming + from the worker. + */ } }) .catch(function (error) { diff --git a/lib/api/core/hotelClerk.js b/lib/api/core/hotelClerk.js index e13816ed44..4f0049c168 100644 --- a/lib/api/core/hotelClerk.js +++ b/lib/api/core/hotelClerk.js @@ -31,6 +31,7 @@ module.exports = function HotelClerk (kuzzle) { * users: 'all|in|out|none' // filter users notifications, default: 'none' * } * }, + * index: 'index', // -> the index name * collection: 'message', // -> the collection name * filters: { * and : { @@ -46,10 +47,11 @@ module.exports = function HotelClerk (kuzzle) { * Example for a customer who subscribes to the room 'chat-room-kuzzle' * customers = { * '87fd-gre7ggth544z' : { // -> connection id (like socket id) - * 'fr4fref4f8fre47fe': { // -> subscribed rooms id - * // metadata for this customer's subscription on that room - * } - * } + * 'fr4fref4f8fre47fe': { // -> subscribed rooms id + * // metadata for this customer's subscription on that room + * } + * } + * } */ this.customers = {}; @@ -68,7 +70,7 @@ module.exports = function HotelClerk (kuzzle) { this.addSubscription = function (requestObject, context) { var deferred = q.defer(); - createRoom.call(this, requestObject.collection, requestObject.data.body) + createRoom.call(this, requestObject.index, requestObject.collection, requestObject.data.body) .then(roomId => { return subscribeToRoom.call(this, roomId, requestObject, context); }) @@ -213,28 +215,33 @@ module.exports = function HotelClerk (kuzzle) { var requestObjectCollection = { action: 'search', - controller: 'read', - index: requestObject.index + controller: 'read' }, collectionNotAllowed = []; _.forEach(this.rooms, (room, roomId) => { - if (collectionNotAllowed.indexOf(room.collection) !== -1) { + if (collectionNotAllowed.indexOf(room.index + '.' + room.collection) !== -1) { return false; } + requestObjectCollection.index = room.index; requestObjectCollection.collection = room.collection; - if (context.user.profile.isActionAllowed(requestObjectCollection, context) === false) { - collectionNotAllowed.push(room.collection); + + if (context.user.profile.isActionAllowed(requestObjectCollection, context, kuzzle.indexes) === false) { + collectionNotAllowed.push(room.index + '.' + room.collection); return true; } - if (!list[room.collection]) { - list[room.collection] = {}; + if (!list[room.index]) { + list[room.index] = {}; } - list[room.collection][roomId] = room.customers.length; + if (!list[room.index][room.collection]) { + list[room.index][room.collection] = {}; + } + + list[room.index][room.collection][roomId] = room.customers.length; }); deferred.resolve(new ResponseObject(requestObject, {body: list})); @@ -273,12 +280,22 @@ module.exports = function HotelClerk (kuzzle) { responseObject, deferred = q.defer(); + if (!requestObject.index) { + deferred.reject(new BadRequestError('No index provided')); + return deferred.promise; + } + if (!requestObject.collection) { deferred.reject(new BadRequestError('No collection provided')); return deferred.promise; } - if (!kuzzle.dsl.filtersTree[requestObject.collection]) { + if (!kuzzle.dsl.filtersTree[requestObject.index]) { + deferred.reject(new NotFoundError('No subscription on this index')); + return deferred.promise; + } + + if (!kuzzle.dsl.filtersTree[requestObject.index][requestObject.collection]) { deferred.reject(new NotFoundError('No subscription on this collection')); return deferred.promise; } @@ -289,7 +306,7 @@ module.exports = function HotelClerk (kuzzle) { return deferred.promise; } - removeListRoomsInCollection.call(this, requestObject.collection, requestObject.data.body.rooms) + removeListRoomsInCollection.call(this, requestObject.index, requestObject.collection, requestObject.data.body.rooms) .then((partialErrors) => { responseObject = new ResponseObject(requestObject, {body: {acknowledge: true}}); if (partialErrors.length > 0) { @@ -304,7 +321,7 @@ module.exports = function HotelClerk (kuzzle) { }); } else { - removeAllRoomsInCollection.call(this, requestObject.collection) + removeAllRoomsInCollection.call(this, requestObject.index, requestObject.collection) .then(() => { return deferred.resolve(new ResponseObject(requestObject, {body: {acknowledge: true}})); }) @@ -324,10 +341,11 @@ module.exports = function HotelClerk (kuzzle) { * Remove all rooms for provided collection * Will remove room from dsl.filtersTree, hotelClerk.rooms and for each customers. * + * @param index * @param collection * @returns {Promise} resolve nothing */ -function removeAllRoomsInCollection (collection) { +function removeAllRoomsInCollection (index, collection) { var deferred = q.defer(); @@ -335,11 +353,11 @@ function removeAllRoomsInCollection (collection) { async.parallel([ // remove rooms from global subscription callbackParallel => { - if (!this.kuzzle.dsl.filtersTree[collection].rooms) { + if (!this.kuzzle.dsl.filtersTree[index][collection].rooms) { return callbackParallel(); } - async.each(this.kuzzle.dsl.filtersTree[collection].rooms, (roomId, callback) => { + async.each(this.kuzzle.dsl.filtersTree[index][collection].rooms, (roomId, callback) => { removeRoomEverywhere.call(this, roomId) .then(() => { callback(); @@ -353,11 +371,11 @@ function removeAllRoomsInCollection (collection) { }, // remove rooms from each fields callbackParallel => { - if (!this.kuzzle.dsl.filtersTree[collection].fields) { + if (!this.kuzzle.dsl.filtersTree[index][collection].fields) { return callbackParallel(); } - _.forEach(this.kuzzle.dsl.filtersTree[collection].fields, field => { + _.forEach(this.kuzzle.dsl.filtersTree[index][collection].fields, field => { _.forEach(field, curryFunction => { async.each(curryFunction.rooms, (roomId, callback) => { removeRoomEverywhere.call(this, roomId) @@ -384,7 +402,7 @@ function removeAllRoomsInCollection (collection) { return deferred.promise; } -function removeListRoomsInCollection (collection, rooms) { +function removeListRoomsInCollection (index, collection, rooms) { var deferred = q.defer(), partialErrors = []; @@ -396,6 +414,12 @@ function removeListRoomsInCollection (collection, rooms) { return callback(); } + if (this.rooms[roomId].index !== index) { + // don't stop the loop if error occured but return a partial error to user + partialErrors.push('The room with id ' + roomId + ' doesn\'t correspond to index ' + index); + return callback(); + } + if (this.rooms[roomId].collection !== collection) { // don't stop the loop if error occured but return a partial error to user partialErrors.push('The room with id ' + roomId + ' doesn\'t correspond to collection ' + collection); @@ -423,11 +447,12 @@ function removeListRoomsInCollection (collection, rooms) { /** * Create new room if needed * + * @param {String} index * @param {String} collection * @param {Object} filters * @returns {Promise} promise */ -function createRoom (collection, filters) { +function createRoom (index, collection, filters) { var deferred = q.defer(), self = this, @@ -446,10 +471,10 @@ function createRoom (collection, filters) { return String(a[0]).localeCompare(b[0]); })); - stringifiedObject = stringify({collection: collection, filters: stringifiedFilters}); + stringifiedObject = stringify({index: index, collection: collection, filters: stringifiedFilters}); } else { - stringifiedObject = stringify({collection: collection, filters: filters}); + stringifiedObject = stringify({index: index, collection: collection, filters: filters}); } roomId = crypto.createHash('md5').update(stringifiedObject).digest('hex'); @@ -461,14 +486,15 @@ function createRoom (collection, filters) { if (!self.rooms[roomId]) { // If it's a new room, we have to calculate filters to apply on the future documents - addRoomAndFilters.call(self, roomId, collection, filters) + addRoomAndFilters.call(self, roomId, index, collection, filters) .then(formattedFilters => { if (!self.rooms[roomId]) { - self.kuzzle.pluginsManager.trigger('room:new', {roomId: roomId, collection: collection, filters: filters}); + self.kuzzle.pluginsManager.trigger('room:new', {roomId: roomId, index: index, collection: collection, filters: filters}); self.rooms[roomId] = { id: roomId, customers: [], + index: index, channels: {}, collection: collection }; @@ -585,6 +611,7 @@ function cleanUpRooms (roomId) { deferred.reject(error); }) .finally(() => delete this.rooms[roomId]); + } else { deferred.resolve(roomId); @@ -650,6 +677,7 @@ function removeRoomForCustomer (connection, roomId) { requestObject = new RequestObject({ controller: 'subscribe', action: 'off', + index: this.rooms[roomId].index, metadata: this.customers[connection.id][roomId] }, null, connection.type); @@ -713,16 +741,17 @@ function removeRoomForAllCustomers (roomId) { * And inject it in the right place in filtersTree according to the collection and field * * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filters * @return {promise} promise. Resolve a list of path that points to filtersTree object */ -function addRoomAndFilters (roomId, collection, filters) { +function addRoomAndFilters (roomId, index, collection, filters) { if (!filters || _.isEmpty(filters)) { - return this.kuzzle.dsl.addCollectionSubscription(roomId, collection); + return this.kuzzle.dsl.addCollectionSubscription(roomId, index, collection); } - return this.kuzzle.dsl.addCurriedFunction(roomId, collection, filters); + return this.kuzzle.dsl.addCurriedFunction(roomId, index, collection, filters); } /** diff --git a/lib/api/core/models/repositories/profileRepository.js b/lib/api/core/models/repositories/profileRepository.js index 4ab476fa49..4372b07d85 100644 --- a/lib/api/core/models/repositories/profileRepository.js +++ b/lib/api/core/models/repositories/profileRepository.js @@ -10,7 +10,8 @@ module.exports = function (kuzzle) { } ProfileRepository.prototype = new Repository(kuzzle, { - collection: '%kuzzle/profiles', + index: '%kuzzle', + collection: 'profiles', ObjectConstructor: Profile, cacheEngine: kuzzle.services.list.userCache }); diff --git a/lib/api/core/models/repositories/repository.js b/lib/api/core/models/repositories/repository.js index 8a5eab565d..838745d877 100644 --- a/lib/api/core/models/repositories/repository.js +++ b/lib/api/core/models/repositories/repository.js @@ -7,6 +7,7 @@ var function Repository (kuzzle, options) { this.collection = options.collection; + this.index = options.index; this.ttl = options.ttl || kuzzle.config.repositories.cacheTTL; this.ObjectConstructor = options.ObjectConstructor; @@ -30,13 +31,14 @@ Repository.prototype.loadOneFromDatabase = function (id) { controller: 'read', action: 'get', collection: this.collection, + index: this.index, body: { _id: id } }); this.readEngine.get(requestObject) - .then(function (response) { + .then(response => { if (response.data) { result = new this.ObjectConstructor(); deferred.resolve(this.hydrate(result, response)); @@ -44,7 +46,7 @@ Repository.prototype.loadOneFromDatabase = function (id) { else { deferred.resolve(null); } - }.bind(this)) + }) .catch(function (error) { if (error.status === 404) { // no content found, we return null without failing @@ -73,16 +75,17 @@ Repository.prototype.loadMultiFromDatabase = function (ids) { controller: 'read', action: 'mget', collection: this.collection, + index: this.index, body: { ids: ids } }); this.readEngine.mget(requestObject) - .then(function (response) { + .then(response => { promises = []; - response.data.docs.forEach(function (document) { + response.data.docs.forEach(document => { var object, data; @@ -94,10 +97,10 @@ Repository.prototype.loadMultiFromDatabase = function (ids) { promises.push(this.hydrate(object, data)); } - }.bind(this)); + }); deferred.resolve(q.all(promises)); - }.bind(this)) + }) .catch(function (error) { deferred.reject(error); }); @@ -121,10 +124,10 @@ Repository.prototype.loadFromCache = function (id, opts) { var deferred = q.defer(), options = opts || {}, - key = options.key || this.collection + '/' + id; + key = options.key || this.index + '/' + this.collection + '/' + id; this.cacheEngine.get(key) - .then(function (response) { + .then(response => { var object = new this.ObjectConstructor(); if (response === null) { @@ -139,7 +142,7 @@ Repository.prototype.loadFromCache = function (id, opts) { .catch(function (error) { deferred.reject(error); }); - }.bind(this)) + }) .catch(function (error) { deferred.reject(error); }); @@ -246,6 +249,7 @@ Repository.prototype.persistToDatabase = function (object) { controller: 'write', action: 'createOrUpdate', collection: this.collection, + index: this.index, body: this.serializeToDatabase(object) }); @@ -265,7 +269,7 @@ Repository.prototype.persistToDatabase = function (object) { Repository.prototype.persistToCache = function (object, opts) { var options = opts || {}, - key = options.key || this.collection + '/' + object._id, + key = options.key || this.index + '/' + this.collection + '/' + object._id, ttl = this.ttl; if (options.ttl !== undefined) { @@ -282,7 +286,7 @@ Repository.prototype.persistToCache = function (object, opts) { Repository.prototype.refreshCacheTTL = function (object, opts) { var options = opts || {}, - key = options.key || this.collection + '/' + object._id, + key = options.key || this.index + '/' + this.collection + '/' + object._id, ttl = this.ttl; if (options.ttl !== undefined) { diff --git a/lib/api/core/models/repositories/roleRepository.js b/lib/api/core/models/repositories/roleRepository.js index 4452115800..bc2eeb0d79 100644 --- a/lib/api/core/models/repositories/roleRepository.js +++ b/lib/api/core/models/repositories/roleRepository.js @@ -12,7 +12,8 @@ module.exports = function (kuzzle) { } RoleRepository.prototype = new Repository(kuzzle, { - collection: '%kuzzle/roles', + index: '%kuzzle', + collection: 'roles', ObjectConstructor: Role, cacheEngine: kuzzle.services.list.userCache }); diff --git a/lib/api/core/models/repositories/userRepository.js b/lib/api/core/models/repositories/userRepository.js index f8ee3945ad..2e3c532cbf 100644 --- a/lib/api/core/models/repositories/userRepository.js +++ b/lib/api/core/models/repositories/userRepository.js @@ -19,7 +19,8 @@ module.exports = function (kuzzle) { } UserRepository.prototype = new Repository(kuzzle, { - collection: '%kuzzle/users', + index: '%kuzzle', + collection: 'users', ObjectConstructor: User, cacheEngine: kuzzle.services.list.userCache }); diff --git a/lib/api/core/models/requestObject.js b/lib/api/core/models/requestObject.js index 69f7bf64c5..d9ff37d994 100644 --- a/lib/api/core/models/requestObject.js +++ b/lib/api/core/models/requestObject.js @@ -22,13 +22,18 @@ function RequestObject(object, additionalData, protocol) { construct.call(this, object, additionalData, protocol); } - +/** + * @param object + * @param additionalData + * @param protocol + * @returns {*} + */ function construct (object, additionalData, protocol) { if (!additionalData) { additionalData = {}; } - ['action', 'controller', 'collection'].forEach(attr => { + ['action', 'controller', 'collection', 'index'].forEach(attr => { this[attr] = object[attr] || additionalData[attr]; }); diff --git a/lib/api/core/models/responseObject.js b/lib/api/core/models/responseObject.js index a1a352f682..59c211197f 100644 --- a/lib/api/core/models/responseObject.js +++ b/lib/api/core/models/responseObject.js @@ -9,6 +9,7 @@ function ResponseObject (requestObject, response) { this.protocol = null; this.action = null; this.collection = null; + this.index = null; this.controller = null; this.requestId = null; this.error = null; @@ -52,6 +53,7 @@ function construct (requestObject, response) { this.protocol = requestObject.protocol; this.action = requestObject.action; this.collection = requestObject.collection; + this.index = requestObject.index; this.controller = requestObject.controller; this.requestId = requestObject.requestId; this.timestamp = requestObject.timestamp; @@ -131,6 +133,7 @@ ResponseObject.prototype.toJson = function (blackList) { formattedData.controller = this.controller; formattedData.action = this.action; formattedData.collection = this.collection; + formattedData.index = this.index; formattedData.metadata = this.metadata; formattedData._source = formattedData._source || formattedData.body; formattedData.state = this.state; diff --git a/lib/api/core/models/security/profile.js b/lib/api/core/models/security/profile.js index 0429fc97b4..28b3659b99 100644 --- a/lib/api/core/models/security/profile.js +++ b/lib/api/core/models/security/profile.js @@ -5,11 +5,13 @@ function Profile () { /** - * @param database + * @param requestObject + * @param context + * @param indexes * * @return {boolean} */ -Profile.prototype.isActionAllowed = function (requestObject, context) { +Profile.prototype.isActionAllowed = function (requestObject, context, indexes) { var result = false; if (this.roles === undefined || this.roles.length === 0) { @@ -17,7 +19,7 @@ Profile.prototype.isActionAllowed = function (requestObject, context) { } result = this.roles.some(function (role) { - return role.isActionAllowed(requestObject, context); + return role.isActionAllowed(requestObject, context, indexes); }); return result; diff --git a/lib/api/core/models/security/role.js b/lib/api/core/models/security/role.js index cb817f0f1d..caa97c0758 100644 --- a/lib/api/core/models/security/role.js +++ b/lib/api/core/models/security/role.js @@ -12,7 +12,21 @@ function Role () { this.closures = {}; } -Role.prototype.isActionAllowed = function (requestObject, context) { +function doesIndexExist(requestObject, indexes) { + return indexes[requestObject.index] !== undefined; +} + +function doesCollectionExist(requestObject, indexes) { + return _.contains(indexes[requestObject.index], requestObject.collection); +} + +/** + * @param requestObject + * @param context + * @param indexes + * @returns {*} + */ +Role.prototype.isActionAllowed = function (requestObject, context, indexes) { var indexRights, collectionRights, @@ -39,6 +53,14 @@ Role.prototype.isActionAllowed = function (requestObject, context) { return false; } + if (this.indexes._canCreate !== undefined && + !this.indexes._canCreate && + _.contains(['import', 'create', 'createCollection', 'putMapping', 'createOrUpdate'], requestObject.action) && + !doesIndexExist(requestObject, indexes)) { + return false; + } + + if (indexRights.collections === undefined) { return false; } @@ -55,6 +77,13 @@ Role.prototype.isActionAllowed = function (requestObject, context) { return false; } + if (indexRights.collections._canCreate !== undefined && + !indexRights.collections._canCreate && + _.contains(['import', 'create', 'createCollection', 'putMapping', 'createOrUpdate'], requestObject.action) && + !doesCollectionExist(requestObject, indexes)) { + return false; + } + if (collectionRights.controllers === undefined) { return false; } @@ -131,51 +160,67 @@ Role.prototype.validateDefinition = function (context) { Object.keys(this.indexes).every(indexKey => { var indexRights = this.indexes[indexKey]; - - if (!_.isObject(indexRights)) { - deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. Must be an object')); - return false; - } - if (_.isEmpty(indexRights)) { - deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. Cannot be empty')); - return false; - } - if (indexRights.collections === undefined) { - deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. `collections` attribute missing')); - return false; - } - if (!_.isObject(indexRights.collections)) { - deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. `collections` attribute must be an object')); - return false; - } - if (_.isEmpty(indexRights.collections)) { - deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. `collections` attribute cannot be empty')); - return false; + + if (_.contains(['_canCreate'], indexKey)) { + if (!_.isBoolean(indexRights)) { + deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. Must be an boolean')); + return false; + } } - - Object.keys(indexRights.collections).every(collectionKey => { - var collectionRights = indexRights.collections[collectionKey]; - - if (!_.isObject(collectionRights)) { - deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. Must be an object')); + else { + if (!_.isObject(indexRights)) { + deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. Must be an object')); return false; } - if (_.isEmpty(collectionRights)) { - deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. Cannot be empty')); + if (_.isEmpty(indexRights)) { + deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. Cannot be empty')); return false; } - if (collectionRights.controllers === undefined) { - deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. `controllers` attribute missing')); + if (indexRights.collections === undefined) { + deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. `collections` attribute missing')); return false; } - if (!_.isObject(collectionRights.controllers)) { - deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. `controllers` attribute must be an object')); + if (!_.isObject(indexRights.collections)) { + deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. `collections` attribute must be an object')); return false; } - if (_.isEmpty(collectionRights.controllers)) { - deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. `controllers` attribute cannot be empty')); + if (_.isEmpty(indexRights.collections)) { + deferred.reject(new BadRequestError('Invalid index definition for ' + indexKey + '. `collections` attribute cannot be empty')); return false; } + } + + Object.keys(indexRights.collections).every(collectionKey => { + var collectionRights = indexRights.collections[collectionKey]; + + if (_.contains(['_canCreate'], collectionKey)) { + if (!_.isBoolean(collectionRights)) { + deferred.reject(new BadRequestError('Invalid index definition for ' + [indexKey, collectionKey] + '. Must be an boolean')); + return false; + } + } + else { + if (!_.isObject(collectionRights)) { + deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. Must be an object')); + return false; + } + if (_.isEmpty(collectionRights)) { + deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. Cannot be empty')); + return false; + } + if (collectionRights.controllers === undefined) { + deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. `controllers` attribute missing')); + return false; + } + if (!_.isObject(collectionRights.controllers)) { + deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. `controllers` attribute must be an object')); + return false; + } + if (_.isEmpty(collectionRights.controllers)) { + deferred.reject(new BadRequestError('Invalid definition for ' + [indexKey, collectionKey] + '. `controllers` attribute cannot be empty')); + return false; + } + } Object.keys(collectionRights.controllers).every(controllerKey => { var controllerRights = collectionRights.controllers[controllerKey]; diff --git a/lib/api/core/notifier.js b/lib/api/core/notifier.js index 2ce45a35e1..7f8bc8c4d3 100644 --- a/lib/api/core/notifier.js +++ b/lib/api/core/notifier.js @@ -124,7 +124,7 @@ function workerNotification (serializedResponseObject) { var responseObject = ResponseObject.prototype.unserialize(serializedResponseObject); var action = { create: notifyDocumentCreate, - createOrUpdate: responseObject.data.created ? notifyDocumentCreate : notifyDocumentUpdate, + createOrUpdate: (responseObject.data && responseObject.data.created) ? notifyDocumentCreate : notifyDocumentUpdate, update: notifyDocumentUpdate, delete: notifyDocumentDelete, deleteByQuery: notifyDocumentDelete @@ -182,6 +182,7 @@ function notifyDocumentUpdate (responseObject) { action: 'update', controller: 'write', collection: responseObject.collection, + index: responseObject.index, _id: responseObject.data._id, requestId: responseObject.requestId, metadata: responseObject.metadata diff --git a/lib/api/core/responseListener.js b/lib/api/core/responseListener.js index dc2f628e44..0c4a5debe1 100644 --- a/lib/api/core/responseListener.js +++ b/lib/api/core/responseListener.js @@ -3,7 +3,7 @@ var ResponseObject = require('./models/responseObject'); module.exports = function ResponseListener (kuzzle, responseQueue) { var whitelist = { - admin: ['deleteCollection', 'putMapping', 'truncateCollection'], + admin: ['deleteCollection', 'putMapping', 'truncateCollection', 'createIndex', 'deleteIndex', 'deleteIndexes'], bulk: ['import'], write: ['create', 'createOrUpdate', 'update', 'delete', 'deleteByQuery', 'createCollection'] }; @@ -47,7 +47,6 @@ function startListener(kuzzle, responseQueue) { requestId = responseObject.requestId; if (requestId && this.waitingQueries[requestId]) { - logObject.testingParam = responseObject.data.body.testingParam; logObject.duration = new Date() - this.waitingQueries[requestId].startTime; kuzzle.emit(responseObject.controller + ':' + responseObject.protocol + ':stop', logObject); diff --git a/lib/api/dsl/geoutil.js b/lib/api/dsl/geoutil.js new file mode 100644 index 0000000000..9e738992d6 --- /dev/null +++ b/lib/api/dsl/geoutil.js @@ -0,0 +1,230 @@ +var + _ = require('lodash'), + BadRequestError = require('../core/errors/badRequestError'), + geohash = require('ngeohash'), + units = require('node-units'), + geoUtil; + +module.exports = geoUtil = { + /** + * Construct a valid usable BBox + * + * @param {Object} geoFilter the given object + * @return {Object} the valid usable BBox object + */ + constructBBox: function (geoFilter) { + var top, left, bottom, right, tmp; + // { top: -74.1, left: 40.73, bottom: -71.12, right: 40.01 } + if (geoFilter.top && + geoFilter.left && + geoFilter.bottom && + geoFilter.right + ) { + top = geoFilter.top; + left = geoFilter.left; + bottom = geoFilter.bottom; + right = geoFilter.right; + } + // { topLeft: { lat: 40.73, lon: -74.1 }, bottomRight: { lat: 40.01, lon: -71.12 } } + else if (geoFilter.topLeft && + geoFilter.bottomRight && + geoFilter.topLeft.lat && + geoFilter.topLeft.lon && + geoFilter.bottomRight.lat && + geoFilter.bottomRight.lon + ) { + top = geoFilter.topLeft.lon; + left = geoFilter.topLeft.lat; + bottom = geoFilter.bottomRight.lon; + right = geoFilter.bottomRight.lat; + } + // { topLeft: [ -74.1, 40.73 ], bottomRight: [ -71.12, 40.01 ] } + else if (geoFilter.topLeft && + geoFilter.bottomRight && + _.isArray(geoFilter.topLeft) && + _.isArray(geoFilter.bottomRight) + ) { + top = geoFilter.topLeft[0]; + left = geoFilter.topLeft[1]; + bottom = geoFilter.bottomRight[0]; + right = geoFilter.bottomRight[1]; + } + // { topLeft: "40.73, -74.1", bottomRight: "40.01, -71.12" } + else if (geoFilter.topLeft && + geoFilter.bottomRight && + _.isString(geoFilter.topLeft) && + _.isString(geoFilter.bottomRight) && + /^[-.0-9]+,\s*[-.0-9]+$/.test(geoFilter.topLeft) && + /^[-.0-9]+,\s*[-.0-9]+$/.test(geoFilter.bottomRight) + ) { + tmp = geoFilter.topLeft.match(/^([-.0-9]+),\s*([-.0-9]+)$/); + top = tmp[2]; + left = tmp[1]; + + tmp = geoFilter.bottomRight.match(/^([-.0-9]+),\s*([-.0-9]+)$/); + bottom = tmp[2]; + right = tmp[1]; + } + // { topLeft: "dr5r9ydj2y73", bottomRight: "drj7teegpus6" } + else if (geoFilter.topLeft && + geoFilter.bottomRight && + _.isString(geoFilter.topLeft) && + _.isString(geoFilter.bottomRight) && + /^[0-9a-z]{4,}$/.test(geoFilter.topLeft) && + /^[0-9a-z]{4,}$/.test(geoFilter.bottomRight) + ) { + tmp = geohash.decode(geoFilter.topLeft); + top = tmp.longitude; + left = tmp.latitude; + + tmp = geohash.decode(geoFilter.bottomRight); + bottom = tmp.longitude; + right = tmp.latitude; + } + + if (top && left && bottom && right) { + if (!_.isNumber(top)) { + top = parseFloat(top); + } + if (!_.isNumber(left)) { + left = parseFloat(left); + } + if (!_.isNumber(bottom)) { + bottom = parseFloat(bottom); + } + if (!_.isNumber(right)) { + right = parseFloat(right); + } + } + else { + throw new BadRequestError('Unable to parse coordinates'); + } + + return {top: top, left: left, bottom: bottom, right: right}; + }, + + /** + * Construct a valid usable BBox + * + * @param {Object} geoFilter the given object + * @return {Object} the valid usable BBox object + */ + constructPolygon: function (geoFilter) { + var point, + polygon = []; + + if (geoFilter.points === undefined) { + throw new BadRequestError('No point list found'); + } + + if (!_.isArray(geoFilter.points)) { + throw new BadRequestError('A polygon must be in array format'); + } + + if (geoFilter.points.length < 3) { + throw new BadRequestError('A polygon must have at least 3 points'); + } + + geoFilter.points.forEach(function (entry) { + point = geoUtil.constructPoint(entry); + polygon.push(point); + }); + + return polygon; + }, + + /** + * Construct a valid usable point + * + * @param {Object} geoFilter the given object + * @return {Object} the valid usable point object + */ + constructPoint: function (geoFilter) { + var lat, lon, tmp; + + // { lat: -74.1, lon: 40.73 } + if (geoFilter.lat !== undefined && + geoFilter.lon !== undefined + ) { + lat = geoFilter.lat; + lon = geoFilter.lon; + } + // { latLon: { lat: 40.73, lon: -74.1 } } + else if (geoFilter.latLon && + geoFilter.latLon.lat !== undefined && + geoFilter.latLon.lon !== undefined + ) { + lat = geoFilter.latLon.lat; + lon = geoFilter.latLon.lon; + } + // { latLon: [ -74.1, 40.73 ] } + else if (geoFilter.latLon && + _.isArray(geoFilter.latLon) + ) { + lat = geoFilter.latLon[0]; + lon = geoFilter.latLon[1]; + } + // { latLon: "40.73, -74.1" } + else if (geoFilter.latLon && + _.isString(geoFilter.latLon) && + /^[-.0-9]+,\s*[-.0-9]+$/.test(geoFilter.latLon) + ) { + tmp = geoFilter.latLon.match(/^([-.0-9]+),\s*([-.0-9]+)$/); + lat = tmp[2]; + lon = tmp[1]; + } + // { latLon: "dr5r9ydj2y73"} + else if (geoFilter.latLon && + _.isString(geoFilter.latLon) && + /^[0-9a-z]{4,}$/.test(geoFilter.latLon) + ) { + tmp = geohash.decode(geoFilter.latLon); + lat = tmp.latitude; + lon = tmp.longitude; + } else if (_.isArray(geoFilter)) { + lat = geoFilter[0]; + lon = geoFilter[1]; + } + + if (lat !== undefined && lon !== undefined) { + if (!_.isNumber(lat)) { + lat = parseFloat(lat); + } + if (!_.isNumber(lon)) { + lon = parseFloat(lon); + } + } + else { + throw new BadRequestError('Unable to parse coordinates'); + } + return {lat: lat, lon: lon}; + }, + + /** + * Generate a valid usable distance + * + * @param {String} distance the given distance + * @return {Object} the distance in meters + */ + getDistance: function (distance) { + var tmp; + if (_.isString(distance)) { + // just clean enough the distance so that localized notations (like "3 258,55 Ft" instead of "3258.55 ft") + // could be accepted + tmp = distance.replace(/-/, '').replace(/ /, '').replace(/,/, '.').toLowerCase().replace(/([0-9])([a-z])/, '$1 $2'); + + try { + // units.convert validate the string, so that we do not need further cleanup + distance = units.convert(tmp + ' to m'); + } + catch (err) { + throw new BadRequestError('Unable to parse the distance filter parameter'); + } + } + else { + // nothing else, lets assume that the distance is already in meters + } + + return distance; + } +}; \ No newline at end of file diff --git a/lib/api/dsl/index.js b/lib/api/dsl/index.js index 0ddfe73584..873703dd09 100644 --- a/lib/api/dsl/index.js +++ b/lib/api/dsl/index.js @@ -16,13 +16,15 @@ module.exports = function Dsl (kuzzle) { * @example * Example for chat-room-kuzzle (see above) * filtersTree = { - * message : { // -> collection name - * rooms: [] // -> global room to test each time (for a a subscribe on a whole collection, or if 'not exists' filter (see issue #1 on github) - * fields: { - * subject : { // -> attribute where a filter exists - * termSubjectKuzzle : { - * rooms: [ 'f45de4d8ef4f3ze4ffzer85d4fgkzm41'], // -> room id that match this filter - * fn: function () {} // -> function to execute on collection message, on field subject + * app : { // -> index name + * message : { // -> collection name + * rooms: [] // -> global room to test each time (for a a subscribe on a whole collection, or if 'not exists' filter (see issue #1 on github) + * fields: { + * subject : { // -> attribute where a filter exists + * termSubjectKuzzle : { + * rooms: [ 'f45de4d8ef4f3ze4ffzer85d4fgkzm41'], // -> room id that match this filter + * fn: function () {} // -> function to execute on collection message, on field subject + * } * } * } * } @@ -38,11 +40,12 @@ module.exports = function Dsl (kuzzle) { /** * Create a currified function with methods and operators. Will create a new filter on a specific field for a collection * @param {string} roomId + * @param {string} index * @param {string} collection * @param {Object} filters * @returns {promise} the generated method */ - this.addCurriedFunction = function (roomId, collection, filters) { + this.addCurriedFunction = function (roomId, index, collection, filters) { var deferred = q.defer(), filterName, @@ -67,29 +70,34 @@ module.exports = function Dsl (kuzzle) { return deferred.promise; } - return this.methods[privateFilterName](roomId, collection, filters[filterName]); + return this.methods[privateFilterName](roomId, index, collection, filters[filterName]); }; /** * Subscribe a roomId on the whole collection * * @param roomId + * @param index * @param collection */ - this.addCollectionSubscription = function (roomId, collection) { + this.addCollectionSubscription = function (roomId, index, collection) { var deferred = q.defer(); - if (!this.filtersTree[collection]) { - this.filtersTree[collection] = {}; + if (!this.filtersTree[index]) { + this.filtersTree[index] = {}; } - if (!this.filtersTree[collection].rooms) { - this.filtersTree[collection].rooms = []; + if (!this.filtersTree[index][collection]) { + this.filtersTree[index][collection] = {}; } - if (this.filtersTree[collection].rooms.indexOf(roomId) === -1) { - this.filtersTree[collection].rooms.push(roomId); + if (!this.filtersTree[index][collection].rooms) { + this.filtersTree[index][collection].rooms = []; + } + + if (this.filtersTree[index][collection].rooms.indexOf(roomId) === -1) { + this.filtersTree[index][collection].rooms.push(roomId); } deferred.resolve(); @@ -110,13 +118,18 @@ module.exports = function Dsl (kuzzle) { flattenBody = flattenObject(modelObject.data.body), that = this; + if (!modelObject.index) { + deferred.reject(new NotFoundError('The data doesn\'t contain an index')); + return deferred.promise; + } + if (!modelObject.collection) { deferred.reject(new NotFoundError('The data doesn\'t contain a collection')); return deferred.promise; } - // No filters set for this collection : we return an empty list - if (!this.filtersTree[modelObject.collection]) { + // No filters set for this index : we return an empty list + if (!this.filtersTree[modelObject.index] || !this.filtersTree[modelObject.index][modelObject.collection]) { deferred.resolve([]); return deferred.promise; } @@ -194,19 +207,27 @@ module.exports = function Dsl (kuzzle) { function removeRoomFromGlobal (room) { var index; - if (!this.filtersTree[room.collection] || !this.filtersTree[room.collection].rooms || _.isEmpty(this.filtersTree[room.collection].rooms)) { + if (!this.filtersTree[room.index] || + !this.filtersTree[room.index][room.collection] || + !this.filtersTree[room.index][room.collection].rooms || + _.isEmpty(this.filtersTree[room.index][room.collection].rooms)) { return false; } - index = this.filtersTree[room.collection].rooms.indexOf(room.id); + index = this.filtersTree[room.index][room.collection].rooms.indexOf(room.id); if (index !== -1) { - this.filtersTree[room.collection].rooms.splice(index, 1); + this.filtersTree[room.index][room.collection].rooms.splice(index, 1); } // Check if we can delete the collection - if (this.filtersTree[room.collection].rooms.length === 0 && - (!this.filtersTree[room.collection].fields || Object.keys(this.filtersTree[room.collection].fields).length === 0)) { - delete this.filtersTree[room.collection]; + if (this.filtersTree[room.index][room.collection].rooms.length === 0 && + (!this.filtersTree[room.index][room.collection].fields || Object.keys(this.filtersTree[room.index][room.collection].fields).length === 0)) { + delete this.filtersTree[room.index][room.collection]; + } + + // Check if we can delete the index + if (Object.keys(this.filtersTree[room.index]).length === 0) { + delete this.filtersTree[room.index]; } } @@ -272,12 +293,15 @@ function testFieldFilters(modelObject, flattenBody, cachedResults) { async.each(documentKeys, function (field, callbackField) { var fieldFilters; - if (!this.filtersTree[modelObject.collection] || !this.filtersTree[modelObject.collection].fields || !this.filtersTree[modelObject.collection].fields[field]) { + if (!this.filtersTree[modelObject.index] || + !this.filtersTree[modelObject.index][modelObject.collection] || + !this.filtersTree[modelObject.index][modelObject.collection].fields || + !this.filtersTree[modelObject.index][modelObject.collection].fields[field]) { callbackField(); return false; } - fieldFilters = this.filtersTree[modelObject.collection].fields[field]; + fieldFilters = this.filtersTree[modelObject.index][modelObject.collection].fields[field]; // For each attribute, loop on all saved filters async.each(Object.keys(fieldFilters), function (functionName, callbackFilter) { @@ -285,7 +309,7 @@ function testFieldFilters(modelObject, flattenBody, cachedResults) { // Clean function name of potential '.' characters cleanFunctionName = functionName.split('.').join(''), filter = fieldFilters[functionName], - cachePath = modelObject.collection + '.' + field + '.' + cleanFunctionName; + cachePath = modelObject.index + '.' + modelObject.collection + '.' + field + '.' + cleanFunctionName; if (cachedResults[cachePath] === undefined) { cachedResults[cachePath] = filter.fn(flattenBody); @@ -379,15 +403,19 @@ function testRooms(roomsToTest, flattenBody, cachedResults) { function testGlobalsFilters(modelObject, flattenBody, cachedResults) { var deferred = q.defer(), + index = modelObject.index, collection = modelObject.collection; // If the entry "rooms" doesn't exist or is an empty array, we don't have a filter to test on every document of this collection - if (!this.filtersTree[collection].rooms || _.isEmpty(this.filtersTree[collection].rooms)) { + if (!this.filtersTree[index] || + !this.filtersTree[index][collection] || + !this.filtersTree[index][collection].rooms || + _.isEmpty(this.filtersTree[index][collection].rooms)) { deferred.resolve([]); return deferred.promise; } - return testRooms.call(this, this.filtersTree[collection].rooms, flattenBody, cachedResults); + return testRooms.call(this, this.filtersTree[index][collection].rooms, flattenBody, cachedResults); } /** diff --git a/lib/api/dsl/methods.js b/lib/api/dsl/methods.js index 9c6a1f57d1..cadac892e2 100644 --- a/lib/api/dsl/methods.js +++ b/lib/api/dsl/methods.js @@ -7,9 +7,8 @@ var q = require('q'), util = require('util'), geohash = require('ngeohash'), - units = require('node-units'), - methods, - geoUtil = {}; + geoUtil = require('./geoutil'), + methods; module.exports = methods = { @@ -22,26 +21,28 @@ module.exports = methods = { * Build rooms and filtersTree according to a given filter for 'term' filter (test equality) * * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filter given by user on subscribe * @param {Boolean} not if not is true, check if filters are not true * @return {Promise} the formatted filter that need to be added to the room */ - term: function (roomId, collection, filter, not) { - return termFunction('term', roomId, collection, filter, not); + term: function (roomId, index, collection, filter, not) { + return termFunction('term', roomId, index, collection, filter, not); }, /** * Build rooms and filtersTree according to a given filter for 'terms' filter (test equality with one of given value in array) * * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filter given by user on subscribe * @param {Boolean} not if not is true, check if filters are not true * @return {Promise} the formatted filter that need to be added to the room */ - terms: function (roomId, collection, filter, not) { - return termFunction('terms', roomId, collection, filter, not); + terms: function (roomId, index, collection, filter, not) { + return termFunction('terms', roomId, index, collection, filter, not); }, /** @@ -49,12 +50,13 @@ module.exports = methods = { * that can contains filters: gte, gt, lte, lt, from, to * * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filter given by user on subscribe * @param {Boolean} not if not is true, check if filters are not true * @return {Promise} the formatted filter that need to be added to the room */ - range: function (roomId, collection, filter, not) { + range: function (roomId, index, collection, filter, not) { var deferred = q.defer(), field, @@ -79,7 +81,7 @@ module.exports = methods = { } curriedFunctionName += 'range' + field + fn + value; - result = buildCurriedFunction(collection, field, fn, value, curriedFunctionName, roomId, not); + result = buildCurriedFunction(index, collection, field, fn, value, curriedFunctionName, roomId, not); if (util.isError(result)) { callback(result); return false; @@ -103,12 +105,13 @@ module.exports = methods = { * Build rooms and filtersTree according to a given filter for 'bool' filter (nested filters with ~and/or) * * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filter given by user on subscribe * @param {Boolean} not if not is true, check if filters are not true * @return {Promise} the formatted filter that need to be added to the room */ - bool: function (roomId, collection, filter, not) { + bool: function (roomId, index, collection, filter, not) { var deferred = q.defer(), allowedBoolFunctions = ['must', 'mustNot', 'should'], @@ -127,7 +130,7 @@ module.exports = methods = { return false; } - this[methodName](roomId, collection, filter[method], not) + this[methodName](roomId, index, collection, filter[method], not) .then(function (subFormattedFilters) { formattedFilters = deepExtend(formattedFilters, subFormattedFilters); callback(); @@ -158,15 +161,16 @@ module.exports = methods = { * Build rooms and filtersTree according to a given filter for 'must' filter (and in nested filters) * * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filters given by user on subscribe * @param {Boolean} not if not is true, check if filters are not true * @return {Promise} the formatted filter that need to be added to the room */ - must: function (roomId, collection, filters, not) { + must: function (roomId, index, collection, filters, not) { var deferred = q.defer(); - getFormattedFilters(roomId, collection, filters, not) + getFormattedFilters(roomId, index, collection, filters, not) .then(function (formattedFilters) { deferred.resolve({and: formattedFilters}); }) @@ -181,49 +185,52 @@ module.exports = methods = { * Build rooms and filtersTree according to a given filter for 'must_not' filter (and not in nested filters) * * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filters given by user on subscribe * @param {Boolean} not if not is true, invert the boolean result * @return {Promise} the formatted filter that need to be added to the room */ - mustNot: function (roomId, collection, filters, not) { + mustNot: function (roomId, index, collection, filters, not) { if (not === undefined) { not = false; } - return this.must(roomId, collection, filters, !not); + return this.must(roomId, index, collection, filters, !not); }, /** * Build rooms and filtersTree according to a given filter for 'should' filter (or in nested filters with a minimum should match option) * * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filters given by user on subscribe * @param {Boolean} not if not is true, invert the boolean result * @return {Promise} the formatted filter that need to be added to the room */ - should: function (roomId, collection, filters, not) { + should: function (roomId, index, collection, filters, not) { if (not) { - return this.and(roomId, collection, filters, not); + return this.and(roomId, index, collection, filters, not); } - return this.or(roomId, collection, filters, not); + return this.or(roomId, index, collection, filters, not); }, /** * Build rooms and filtersTree according to a given filter for 'and' filter * * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filters given by user on subscribe * @param {Boolean} not if not is true, invert the boolean result * @return {Promise} the formatted filter that need to be added to the room */ - and: function (roomId, collection, filters, not) { + and: function (roomId, index, collection, filters, not) { var deferred = q.defer(); - getFormattedFilters(roomId, collection, filters, not) + getFormattedFilters(roomId, index, collection, filters, not) .then(function (formattedFilters) { deferred.resolve({and: formattedFilters}); }) @@ -238,16 +245,17 @@ module.exports = methods = { * Build rooms and filtersTree according to a given filter for 'or' filter * * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filters given by user on subscribe * @param {Boolean} not if not is true, invert the boolean result * @return {Promise} the formatted filter that need to be added to the room */ - or: function (roomId, collection, filters, not) { + or: function (roomId, index, collection, filters, not) { var deferred = q.defer(); - getFormattedFiltersAsList(roomId, collection, filters, not) + getFormattedFiltersAsList(roomId, index, collection, filters, not) .then(formattedFilters => { return deferred.resolve({or: formattedFilters}); }) @@ -262,29 +270,31 @@ module.exports = methods = { * Build rooms and filtersTree according to a given filter for 'not' filter * * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filters given by user on subscribe * @param {Boolean} not if not is true, invert the boolean result * @return {Promise} the formatted filter that need to be added to the room */ - not: function (roomId, collection, filters, not) { + not: function (roomId, index, collection, filters, not) { if (not === undefined) { not = false; } - return this.must(roomId, collection, filters, !not); + return this.must(roomId, index, collection, filters, !not); }, /** * Build rooms and filtersTree according to a given filter for 'exists' filter * * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filter given by user on subscribe * @param {Boolean} not if not is true, invert the boolean result * @return {Promise} the formatted filter that need to be added to the room */ - exists: function (roomId, collection, filter, not) { + exists: function (roomId, index, collection, filter, not) { var deferred = q.defer(), fieldName, @@ -319,7 +329,7 @@ module.exports = methods = { // Clean the field in function name because can contains '.' and we don't want it in the function name curriedFunctionName += 'exists' + fieldName.split('.').join(''); - result = buildCurriedFunction(collection, fieldName, 'exists', fieldName, curriedFunctionName, roomId, not, inGlobals); + result = buildCurriedFunction(index, collection, fieldName, 'exists', fieldName, curriedFunctionName, roomId, not, inGlobals); if (util.isError(result)) { deferred.reject(result); return deferred.promise; @@ -335,12 +345,13 @@ module.exports = methods = { * Build rooms and filtersTree according to a given filter for 'ids' filter * * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filter given by user on subscribe * @param {Boolean} not if not is true, invert the boolean result * @return {Promise} the formatted filter that need to be added to the room */ - ids: function (roomId, collection, filter, not) { + ids: function (roomId, index, collection, filter, not) { var deferred = q.defer(), formattedFilters, @@ -371,7 +382,7 @@ module.exports = methods = { curriedFunctionName += 'ids_id' + filter.values; // We can use the 'terms' operators because is the same behaviour: check if the value in document match one of values in the filter - result = buildCurriedFunction(collection, '_id', 'terms', filter.values, curriedFunctionName, roomId, not, false); + result = buildCurriedFunction(index, collection, '_id', 'terms', filter.values, curriedFunctionName, roomId, not, false); if (util.isError(result)) { deferred.reject(result); @@ -388,12 +399,13 @@ module.exports = methods = { * Build rooms and filtersTree according to a given filter for 'geoBoundingBox' filter * * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filter given by user on subscribe * @param {Boolean} not if not is true, invert the boolean result * @return {Promise} the formatted filter that need to be added to the room */ - geoBoundingBox: function (roomId, collection, filter, not) { + geoBoundingBox: function (roomId, index, collection, filter, not) { var curriedFunctionName, deferred = q.defer(), @@ -446,6 +458,7 @@ module.exports = methods = { } result = buildCurriedFunction( + index, collection, fieldName, 'geoBoundingBox', @@ -469,12 +482,13 @@ module.exports = methods = { /** * Return true only if the point in field is in a specific distance from a geo point * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filter given by user on subscribe * @param {Boolean} not if not is true, invert the boolean result * @return {Promise} the formatted filter that need to be added to the room */ - geoDistance: function (roomId, collection, filter, not) { + geoDistance: function (roomId, index, collection, filter, not) { var curriedFunctionName, deferred = q.defer(), @@ -536,6 +550,7 @@ module.exports = methods = { } result = buildCurriedFunction( + index, collection, fieldName, 'geoDistance', @@ -559,12 +574,13 @@ module.exports = methods = { /** * Return true only if the point in field is in a range from a specific point * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filter given by user on subscribe * @param {Boolean} not if not is true, invert the boolean result * @return {Promise} the formatted filter that need to be added to the room */ - geoDistanceRange: function (roomId, collection, filter, not) { + geoDistanceRange: function (roomId, index, collection, filter, not) { var curriedFunctionName, deferred = q.defer(), @@ -631,6 +647,7 @@ module.exports = methods = { } result = buildCurriedFunction( + index, collection, fieldName, 'geoDistanceRange', @@ -654,12 +671,13 @@ module.exports = methods = { /** * Return true only if the point in field is included in a polygon * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filter given by user on subscribe * @param {Boolean} not if not is true, invert the boolean result * @return {Promise} the formatted filter that need to be added to the room */ - geoPolygon: function (roomId, collection, filter, not) { + geoPolygon: function (roomId, index, collection, filter, not) { var curriedFunctionName, deferred = q.defer(), @@ -699,6 +717,7 @@ module.exports = methods = { } result = buildCurriedFunction( + index, collection, fieldName, 'geoPolygon', @@ -743,12 +762,13 @@ module.exports = methods = { * Build rooms and filtersTree according to a given filter for 'missing' filter * * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filter given by user on subscribe * @param {Boolean} not if not is true, invert the boolean result * @return {Promise} the formatted filter that need to be added to the room */ - missing: function (roomId, collection, filter, not) { + missing: function (roomId, index, collection, filter, not) { var deferred = q.defer(), fieldName, @@ -778,7 +798,7 @@ module.exports = methods = { // Clean the field in function name because can contains '.' and we don't want it in the function name curriedFunctionName += 'missing' + fieldName.split('.').join(''); - result = buildCurriedFunction(collection, fieldName, 'missing', fieldName, curriedFunctionName, roomId, not, inGlobals); + result = buildCurriedFunction(index, collection, fieldName, 'missing', fieldName, curriedFunctionName, roomId, not, inGlobals); if (util.isError(result)) { deferred.reject(result); return deferred.promise; @@ -795,6 +815,7 @@ module.exports = methods = { /** * Fill object filtersTree with the new filter added by user * + * @param {String} index the index name * @param {String} collection the collection name * @param {String} field the field where we need to apply the filter * @param {String} operatorName the operator name that the user wants to execute against the document (defined in operator.js) @@ -805,57 +826,61 @@ module.exports = methods = { * @param {Boolean} inGlobals true if the roomId must be added in global room for the collection (eg, for 'not exists' filter) * @returns {Object} an object with the path and the new filter */ -function buildCurriedFunction(collection, field, operatorName, value, curriedFunctionName, roomId, not, inGlobals) { +function buildCurriedFunction(index, collection, field, operatorName, value, curriedFunctionName, roomId, not, inGlobals) { var curriedFunction, - path = collection + '.' + field + '.' + curriedFunctionName; + path = index + '.' + collection + '.' + field + '.' + curriedFunctionName; if (operators[operatorName] === undefined) { return new BadRequestError('Operator ' + operatorName + ' doesn\'t exist'); } - if (!methods.dsl.filtersTree[collection]) { - methods.dsl.filtersTree[collection] = {}; + if (!methods.dsl.filtersTree[index]) { + methods.dsl.filtersTree[index] = {}; } - if (!methods.dsl.filtersTree[collection].fields) { - methods.dsl.filtersTree[collection].fields = {}; + if (!methods.dsl.filtersTree[index][collection]) { + methods.dsl.filtersTree[index][collection] = {}; } - if (!methods.dsl.filtersTree[collection].fields[field]) { - methods.dsl.filtersTree[collection].fields[field] = {}; + if (!methods.dsl.filtersTree[index][collection].fields) { + methods.dsl.filtersTree[index][collection].fields = {}; } - if (!methods.dsl.filtersTree[collection].fields[field][curriedFunctionName]) { + if (!methods.dsl.filtersTree[index][collection].fields[field]) { + methods.dsl.filtersTree[index][collection].fields[field] = {}; + } + + if (!methods.dsl.filtersTree[index][collection].fields[field][curriedFunctionName]) { curriedFunction = _.curry(operators[operatorName]); curriedFunction = _.curry(curriedFunction(field, value)); if (not) { curriedFunction = _.negate(curriedFunction); } - methods.dsl.filtersTree[collection].fields[field][curriedFunctionName] = { + methods.dsl.filtersTree[index][collection].fields[field][curriedFunctionName] = { rooms: [], fn: curriedFunction }; } - if (methods.dsl.filtersTree[collection].fields[field][curriedFunctionName].rooms.indexOf(roomId) === -1) { - methods.dsl.filtersTree[collection].fields[field][curriedFunctionName].rooms.push(roomId); + if (methods.dsl.filtersTree[index][collection].fields[field][curriedFunctionName].rooms.indexOf(roomId) === -1) { + methods.dsl.filtersTree[index][collection].fields[field][curriedFunctionName].rooms.push(roomId); } if (inGlobals) { - if (!methods.dsl.filtersTree[collection].rooms) { - methods.dsl.filtersTree[collection].rooms = []; + if (!methods.dsl.filtersTree[index][collection].rooms) { + methods.dsl.filtersTree[index][collection].rooms = []; } - if (methods.dsl.filtersTree[collection].rooms.indexOf(roomId) === -1) { - methods.dsl.filtersTree[collection].rooms.push(roomId); + if (methods.dsl.filtersTree[index][collection].rooms.indexOf(roomId) === -1) { + methods.dsl.filtersTree[index][collection].rooms.push(roomId); } } return { path: path, - filter: methods.dsl.filtersTree[collection].fields[field][curriedFunctionName] + filter: methods.dsl.filtersTree[index][collection].fields[field][curriedFunctionName] }; } @@ -863,12 +888,13 @@ function buildCurriedFunction(collection, field, operatorName, value, curriedFun * Construct the formattedFilters for filters with conditional operand (bool, and, or, ...) * * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filters given by user on subscribe * @param {Boolean} not if not is true, invert the boolean result * @return {Promise} the formatted filter that need to be added to the room */ -function getFormattedFilters(roomId, collection, filters, not) { +function getFormattedFilters(roomId, index, collection, filters, not) { var deferred = q.defer(), formattedFilters; @@ -903,7 +929,7 @@ function getFormattedFilters(roomId, collection, filters, not) { return false; } - methods[methodName](roomId, collection, filter[method], not) + methods[methodName](roomId, index, collection, filter[method], not) .then(function (subFormattedFilters) { formattedFilters = deepExtend(formattedFilters, subFormattedFilters); callback(); @@ -927,12 +953,13 @@ function getFormattedFilters(roomId, collection, filters, not) { * Build formattedFilters as list for operand like OR and SHOULD * * @param {string} roomId + * @param {string} index * @param {string} collection * @param {Object} filters * @param {boolean} not * @returns {Promise} */ -function getFormattedFiltersAsList (roomId, collection, filters, not) { +function getFormattedFiltersAsList (roomId, index, collection, filters, not) { var deferred = q.defer(), formattedFilters = []; @@ -942,8 +969,9 @@ function getFormattedFiltersAsList (roomId, collection, filters, not) { return deferred.promise; } + async.each(filters, (filter, callback) => { - getFormattedFilters(roomId, collection, filter, not) + getFormattedFilters(roomId, index, collection, filter, not) .then(formattedFilter => { formattedFilters.push(formattedFilter); callback(); @@ -968,7 +996,7 @@ function getFormattedFiltersAsList (roomId, collection, filters, not) { * @param {Object} filters2 * @returns {Object} the merged object */ -function deepExtend (filters1, filters2) { +function deepExtend(filters1, filters2) { var attr, resultFilters; @@ -1001,12 +1029,13 @@ function deepExtend (filters1, filters2) { * * @param {String} termType "term" or "terms" * @param {String} roomId + * @param {String} index * @param {String} collection * @param {Object} filter given by user on subscribe * @param {Boolean} not if not is true, check if filters are not true * @return {Promise} the formatted filter that need to be added to the room */ -function termFunction (termType, roomId, collection, filter, not) { +function termFunction(termType, roomId, index, collection, filter, not) { var deferred = q.defer(), field, @@ -1035,7 +1064,7 @@ function termFunction (termType, roomId, collection, filter, not) { // Clean the field in function name because can contains '.' and we don't want it in the function name curriedFunctionName += termType + field.split('.').join('') + value; - result = buildCurriedFunction(collection, field, termType, value, curriedFunctionName, roomId, not); + result = buildCurriedFunction(index, collection, field, termType, value, curriedFunctionName, roomId, not); if (util.isError(result)) { deferred.reject(result); return deferred.promise; @@ -1046,227 +1075,3 @@ function termFunction (termType, roomId, collection, filter, not) { deferred.resolve(formattedFilters); return deferred.promise; } - -geoUtil = { - /** - * Construct a valid usable BBox - * - * @param {Object} geoFilter the given object - * @return {Object} the valid usable BBox object - */ - constructBBox: function (geoFilter) { - var top, left, bottom, right, tmp; - // { top: -74.1, left: 40.73, bottom: -71.12, right: 40.01 } - if (geoFilter.top && - geoFilter.left && - geoFilter.bottom && - geoFilter.right - ) { - top = geoFilter.top; - left = geoFilter.left; - bottom = geoFilter.bottom; - right = geoFilter.right; - } - // { topLeft: { lat: 40.73, lon: -74.1 }, bottomRight: { lat: 40.01, lon: -71.12 } } - else if (geoFilter.topLeft && - geoFilter.bottomRight && - geoFilter.topLeft.lat && - geoFilter.topLeft.lon && - geoFilter.bottomRight.lat && - geoFilter.bottomRight.lon - ) { - top = geoFilter.topLeft.lon; - left = geoFilter.topLeft.lat; - bottom = geoFilter.bottomRight.lon; - right = geoFilter.bottomRight.lat; - } - // { topLeft: [ -74.1, 40.73 ], bottomRight: [ -71.12, 40.01 ] } - else if (geoFilter.topLeft && - geoFilter.bottomRight && - _.isArray(geoFilter.topLeft) && - _.isArray(geoFilter.bottomRight) - ) { - top = geoFilter.topLeft[0]; - left = geoFilter.topLeft[1]; - bottom = geoFilter.bottomRight[0]; - right = geoFilter.bottomRight[1]; - } - // { topLeft: "40.73, -74.1", bottomRight: "40.01, -71.12" } - else if (geoFilter.topLeft && - geoFilter.bottomRight && - _.isString(geoFilter.topLeft) && - _.isString(geoFilter.bottomRight) && - /^[-.0-9]+,\s*[-.0-9]+$/.test(geoFilter.topLeft) && - /^[-.0-9]+,\s*[-.0-9]+$/.test(geoFilter.bottomRight) - ) { - tmp = geoFilter.topLeft.match(/^([-.0-9]+),\s*([-.0-9]+)$/); - top = tmp[2]; - left = tmp[1]; - - tmp = geoFilter.bottomRight.match(/^([-.0-9]+),\s*([-.0-9]+)$/); - bottom = tmp[2]; - right = tmp[1]; - } - // { topLeft: "dr5r9ydj2y73", bottomRight: "drj7teegpus6" } - else if (geoFilter.topLeft && - geoFilter.bottomRight && - _.isString(geoFilter.topLeft) && - _.isString(geoFilter.bottomRight) && - /^[0-9a-z]{4,}$/.test(geoFilter.topLeft) && - /^[0-9a-z]{4,}$/.test(geoFilter.bottomRight) - ) { - tmp = geohash.decode(geoFilter.topLeft); - top = tmp.longitude; - left = tmp.latitude; - - tmp = geohash.decode(geoFilter.bottomRight); - bottom = tmp.longitude; - right = tmp.latitude; - } - - if (top && left && bottom && right) { - if (!_.isNumber(top)) { - top = parseFloat(top); - } - if (!_.isNumber(left)) { - left = parseFloat(left); - } - if (!_.isNumber(bottom)) { - bottom = parseFloat(bottom); - } - if (!_.isNumber(right)) { - right = parseFloat(right); - } - } - else { - throw new BadRequestError('Unable to parse coordinates'); - } - - return {top: top, left: left, bottom: bottom, right: right}; - }, - - /** - * Construct a valid usable BBox - * - * @param {Object} geoFilter the given object - * @return {Object} the valid usable BBox object - */ - constructPolygon: function (geoFilter) { - var point, - polygon = []; - - if (geoFilter.points === undefined) { - throw new BadRequestError('No point list found'); - } - - if (!_.isArray(geoFilter.points)) { - throw new BadRequestError('A polygon must be in array format'); - } - - if (geoFilter.points.length < 3) { - throw new BadRequestError('A polygon must have at least 3 points'); - } - - geoFilter.points.forEach(function (entry) { - point = geoUtil.constructPoint(entry); - polygon.push(point); - }); - - return polygon; - }, - - /** - * Construct a valid usable point - * - * @param {Object} geoFilter the given object - * @return {Object} the valid usable point object - */ - constructPoint: function (geoFilter) { - var lat, lon, tmp; - - // { lat: -74.1, lon: 40.73 } - if (geoFilter.lat !== undefined && - geoFilter.lon !== undefined - ) { - lat = geoFilter.lat; - lon = geoFilter.lon; - } - // { latLon: { lat: 40.73, lon: -74.1 } } - else if (geoFilter.latLon && - geoFilter.latLon.lat !== undefined && - geoFilter.latLon.lon !== undefined - ) { - lat = geoFilter.latLon.lat; - lon = geoFilter.latLon.lon; - } - // { latLon: [ -74.1, 40.73 ] } - else if (geoFilter.latLon && - _.isArray(geoFilter.latLon) - ) { - lat = geoFilter.latLon[0]; - lon = geoFilter.latLon[1]; - } - // { latLon: "40.73, -74.1" } - else if (geoFilter.latLon && - _.isString(geoFilter.latLon) && - /^[-.0-9]+,\s*[-.0-9]+$/.test(geoFilter.latLon) - ) { - tmp = geoFilter.latLon.match(/^([-.0-9]+),\s*([-.0-9]+)$/); - lat = tmp[2]; - lon = tmp[1]; - } - // { latLon: "dr5r9ydj2y73"} - else if (geoFilter.latLon && - _.isString(geoFilter.latLon) && - /^[0-9a-z]{4,}$/.test(geoFilter.latLon) - ) { - tmp = geohash.decode(geoFilter.latLon); - lat = tmp.latitude; - lon = tmp.longitude; - } else if (_.isArray(geoFilter)) { - lat = geoFilter[0]; - lon = geoFilter[1]; - } - - if (lat !== undefined && lon !== undefined) { - if (!_.isNumber(lat)) { - lat = parseFloat(lat); - } - if (!_.isNumber(lon)) { - lon = parseFloat(lon); - } - } - else { - throw new BadRequestError('Unable to parse coordinates'); - } - return {lat: lat, lon: lon}; - }, - - /** - * Generate a valid usable distance - * - * @param {String} distance the given distance - * @return {Object} the distance in meters - */ - getDistance: function (distance) { - var tmp; - if (_.isString(distance)) { - // just clean enough the distance so that localized notations (like "3 258,55 Ft" instead of "3258.55 ft") - // could be accepted - tmp = distance.replace(/-/, '').replace(/ /, '').replace(/,/, '.').toLowerCase().replace(/([0-9])([a-z])/, '$1 $2'); - - try { - // units.convert validate the string, so that we do not need further cleanup - distance = units.convert(tmp + ' to m'); - } - catch (err) { - throw new BadRequestError('Unable to parse the distance filter parameter'); - } - } - else { - // nothing else, lets assume that the distance is already in meters - } - - return distance; - } -}; \ No newline at end of file diff --git a/lib/api/prepareDb.js b/lib/api/prepareDb.js index 33311f1386..7ff64fdbfc 100644 --- a/lib/api/prepareDb.js +++ b/lib/api/prepareDb.js @@ -39,30 +39,34 @@ function importMapping() { return deferred.promise; } - async.each(Object.keys(mappings), function (collection, callbackCollection) { - this.pluginsManager.trigger('log:info', '== Importing mapping for collection ' + collection + '...'); - - async.each(mappings[collection], function (mapping, callbackMapping) { - - var mappingOptions = { - action: 'putMapping', - collection: collection, - body: mapping - }; - - this.services.list.writeEngine.putMapping(new RequestObject(mappingOptions)) - .then(function () { - callbackMapping(); - }) - .catch(function (error) { - callbackMapping('Mapping import error' + error); - }); - - }.bind(this), function (error) { - callbackCollection(error); + async.each(Object.keys(mappings), (index, callbackIndex) => { + async.each(Object.keys(mappings[index]), (collection, callbackCollection) => { + this.pluginsManager.trigger('log:info', '== Importing mapping for collection ' + index + ':' + collection + '...'); + + async.each(mappings[index][collection], (mapping, callbackMapping) => { + var mappingOptions = { + action: 'putMapping', + index: index, + collection: collection, + body: mapping + }; + + this.services.list.writeEngine.putMapping(new RequestObject(mappingOptions)) + .then(function () { + callbackMapping(); + }) + .catch(function (error) { + callbackMapping('Mapping import error' + error); + }); + + }, function (error) { + callbackCollection(error); + }); + }, function (error) { + callbackIndex(error); }); - }.bind(this), function (error) { + }, error => { if (error) { return deferred.reject(error); } @@ -70,7 +74,7 @@ function importMapping() { this.pluginsManager.trigger('prepare:MappingEnd', 'All fixtures imports launched.'); return deferred.resolve(); - }.bind(this)); + }); return deferred.promise; } @@ -101,23 +105,27 @@ function importFixtures() { return deferred.promise; } - async.each(Object.keys(fixtures), function (collection, callback) { - var fixture = { - action: 'import', - collection: collection, - body: fixtures[collection] - }; - - this.pluginsManager.trigger('log:info', '== Importing fixtures for collection ' + collection + '...'); - this.services.list.writeEngine.import(new RequestObject(fixture)) - .then(function () { - callback(); - }) - .catch(function (error) { - callback('Fixture import error' + error); - }); + async.each(Object.keys(fixtures), (index, callbackIndex) => { + async.each(Object.keys(fixtures[index]), (collection, callback) => { + var fixture = { + action: 'import', + index: index, + collection: collection, + body: fixtures[index][collection] + }; - }.bind(this), function (error) { + this.pluginsManager.trigger('log:info', '== Importing fixtures for collection ' + collection + '...'); + this.services.list.writeEngine.import(new RequestObject(fixture)) + .then(function () { + callback(); + }) + .catch(function (error) { + callback('Fixture import error' + error); + }); + }, function (error) { + callbackIndex(error); + }); + }, error => { if (error) { deferred.reject(error); } @@ -125,7 +133,7 @@ function importFixtures() { this.pluginsManager.trigger('prepare:fixtureEnd', 'All fixtures imports launched.'); deferred.resolve(); - }.bind(this)); + }); return deferred.promise; } diff --git a/lib/api/start.js b/lib/api/start.js index 3d5cb53d8f..7e990dfec8 100644 --- a/lib/api/start.js +++ b/lib/api/start.js @@ -6,6 +6,7 @@ var q = require('q'), servers = require('./core/servers'), + RequestObject = require('./core/models/requestObject'), HotelClerk = require('./core/hotelClerk'), Notifier = require('./core/notifier'), Statistics = require('./core/statistics'), @@ -56,7 +57,19 @@ module.exports = function start (params, feature) { if (!this.isWorker) { if (!feature.dummy) { - this.services.init({server: true, blacklist: ['perf']}); + this.services.init({server: true}); + + this.services.list.readEngine.listIndexes(new RequestObject({})) + .then(result => { + result.data.indexes.forEach(index => { + this.indexes[index] = []; + + this.services.list.readEngine.listCollections(new RequestObject({index: index})) + .then(resultCollections => { + this.indexes[index] = resultCollections.data.collections; + }); + }); + }); } else { this.services.init({server: false, blacklist: ['mqBroker', 'perf', 'writeEngine', 'readEngine', 'notificationCache', 'monitoring', 'remoteActions', 'statsCache']}); @@ -86,7 +99,6 @@ module.exports = function start (params, feature) { // Starts the servers in charge of listening to client queries (HTTP, MQ or WebSocket) servers.initAll(this, params); - } } diff --git a/lib/config/hooks.js b/lib/config/hooks.js index d1d8082348..c4d8ef94f7 100644 --- a/lib/config/hooks.js +++ b/lib/config/hooks.js @@ -5,11 +5,15 @@ module.exports = { 'data:delete': ['write:add', 'publish:add'], 'data:deleteByQuery': ['write:add'], 'data:bulkImport': ['write:add'], - 'data:deleteCollection': ['write:add'], 'data:putMapping': ['write:add'], - 'data:reset': ['write:add'], 'data:createCollection': ['write:add'], + 'data:deleteCollection': ['write:add'], 'data:truncateCollection': ['write:add'], - 'data:putRole': [], - 'data:publish': [] + 'data:publish': [], + 'data:listIndexes': [], + 'data:createIndex': ['write:add'], + 'data:deleteIndex': ['write:add'], + 'data:deleteIndexes': ['write:add'], + 'data:putRole': [] }; + diff --git a/lib/config/models/readEngine.js b/lib/config/models/readEngine.js index ca7d65152b..1b29d8dec9 100644 --- a/lib/config/models/readEngine.js +++ b/lib/config/models/readEngine.js @@ -2,7 +2,6 @@ module.exports = function (params) { var config = { apiVersion: '1.7', - index: 'mainindex', hosts: [] }; diff --git a/lib/config/models/writeEngine.js b/lib/config/models/writeEngine.js index 288e8fea6a..dd56fb0bd3 100644 --- a/lib/config/models/writeEngine.js +++ b/lib/config/models/writeEngine.js @@ -2,7 +2,6 @@ module.exports = function (params) { var config = { apiVersion: '1.7', - index: 'mainindex', hosts: [] }; diff --git a/lib/services/elasticsearch.js b/lib/services/elasticsearch.js index d6bc8b9adc..343a82f53c 100644 --- a/lib/services/elasticsearch.js +++ b/lib/services/elasticsearch.js @@ -6,7 +6,8 @@ var BadRequestError = require('../api/core/errors/badRequestError'), NotFoundError = require('../api/core/errors/notFoundError'), PartialError = require('../api/core/errors/partialError'), - es = require('elasticsearch'); + es = require('elasticsearch'), + indexCache; /** * @@ -147,9 +148,21 @@ module.exports = function (kuzzle, options) { * @returns {Promise} resolve an object that contains _id */ this.create = function (requestObject) { - var data = cleanData.call(this, requestObject); + var + deferred = q.defer(), + data = cleanData.call(this, requestObject); + + this.client.create(data) + .then(function (result) { + indexCache.add.call(kuzzle, data.index, data.type); + + deferred.resolve(new ResponseObject(requestObject, result)); + }) + .catch(function (error) { + deferred.reject(error); + }); - return this.client.create(data); + return deferred.promise; }; /** @@ -159,9 +172,21 @@ module.exports = function (kuzzle, options) { * @returns {Promise} resolve an object that contains _id */ this.createOrUpdate = function (requestObject) { - var data = cleanData.call(this, requestObject); + var + deferred = q.defer(), + data = cleanData.call(this, requestObject); + + this.client.index(data) + .then(function (result) { + indexCache.add.call(kuzzle, data.index, data.type); - return this.client.index(data); + deferred.resolve(new ResponseObject(requestObject, result)); + }) + .catch(function (error) { + deferred.reject(error); + }); + + return deferred.promise; }; /** @@ -172,10 +197,23 @@ module.exports = function (kuzzle, options) { * @returns {Promise} resolve an object that contains _id */ this.update = function (requestObject) { - var data = cleanData.call(this, requestObject); + var + deferred = q.defer(), + data = cleanData.call(this, requestObject); + data.body = {doc: data.body}; - return this.client.update(data); + this.client.update(data) + .then(function (result) { + indexCache.add.call(kuzzle, data.index, data.type); + + deferred.resolve(new ResponseObject(requestObject, result)); + }) + .catch(function (error) { + deferred.reject(error); + }); + + return deferred.promise; }; /** @@ -185,9 +223,19 @@ module.exports = function (kuzzle, options) { * @returns {Promise} resolve an object that contains _id */ this.delete = function (requestObject) { - var data = cleanData.call(this, requestObject); + var + deferred = q.defer(), + data = cleanData.call(this, requestObject); - return this.client.delete(data); + this.client.delete(data) + .then(function (result) { + deferred.resolve(new ResponseObject(requestObject, result)); + }) + .catch(function (error) { + deferred.reject(error); + }); + + return deferred.promise; }; /** @@ -212,13 +260,13 @@ module.exports = function (kuzzle, options) { }, function () { if (bodyBulk.length === 0) { - deferred.resolve({ids : []}); + deferred.resolve(new ResponseObject(requestObject, {ids : []})); return false; } this.client.bulk({body: bodyBulk}) .then(function () { - deferred.resolve({ids: ids}); + deferred.resolve(new ResponseObject(requestObject, {ids : ids})); }) .catch(function (error) { deferred.reject(error); @@ -245,6 +293,7 @@ module.exports = function (kuzzle, options) { this.client.indices.deleteMapping(data) .then(function () { + indexCache.remove.call(kuzzle, data.index, data.type); deferred.resolve(new ResponseObject(requestObject)); }) .catch(function (error) { @@ -261,12 +310,23 @@ module.exports = function (kuzzle, options) { * @returns {Promise} */ this.createCollection = function (requestObject) { - var data = cleanData.call(this, requestObject); + var + deferred = q.defer(), + data = cleanData.call(this, requestObject); data.body = {}; data.body[data.type] = {}; - return this.client.indices.putMapping(data); + this.client.indices.putMapping(data) + .then(function () { + indexCache.add.call(kuzzle, data.index, data.type); + deferred.resolve(new ResponseObject(requestObject)); + }) + .catch(function (error) { + deferred.reject(error); + }); + + return deferred.promise; }; /** @@ -285,11 +345,11 @@ module.exports = function (kuzzle, options) { this.client.indices.getMapping(data) .then(mapping => { - if (!mapping[this.kuzzleConfig[this.engineType].index]) { - return Promise.reject(new NotFoundError('The collection "' + data.type + '" does not exist')); + if (!mapping[data.index]) { + return Promise.reject(new NotFoundError('The collection "' + data.type + '" in index "' + data.index + '" does not exist')); } - mappings = mapping[this.kuzzleConfig[this.engineType].index].mappings[data.type]; + mappings = mapping[data.index].mappings[data.type]; return this.client.indices.deleteMapping(data); }) .then(() => { @@ -298,7 +358,9 @@ module.exports = function (kuzzle, options) { return this.client.indices.putMapping(data); }) - .then(result => deferred.resolve(new ResponseObject(requestObject, result))) + .then(result => { + deferred.resolve(new ResponseObject(requestObject, result)); + }) .catch(error => deferred.reject(error)); return deferred.promise; @@ -316,50 +378,71 @@ module.exports = function (kuzzle, options) { data = cleanData.call(this, requestObject); if (data.body) { - // override index - async.eachLimit(data.body, 20, function (item) { + // set missing index & type if possible + async.eachLimit(data.body, 20, item => { var action = Object.keys(item)[0]; if (nameActions.indexOf(action) !== -1) { - if (data.type && data.type.length > 0) { + if (!item[action]._type && data.type !== undefined) { item[action]._type = data.type; } - else if (!item[action]._type) { - return Promise.reject(new BadRequestError('Missing data collection argument')); + if (!item[action]._type) { + deferred.reject(new BadRequestError('Missing data collection argument')); + return false; } - // TODO: implement multi index - item[action]._index = this.kuzzleConfig[this.engineType].index; + if (!item[action]._index && data.index !== undefined) { + item[action]._index = data.index; + } + if (!item[action]._index) { + deferred.reject(new BadRequestError('Missing data collection argument')); + return false; + } } - }.bind(this)); - this.client.bulk(data) - .then(function (result) { - var - responseObject, - stack; - - responseObject = new ResponseObject(requestObject, result); - // If some errors occured during the Bulk, we send a "Partial Error" response : - if (result.errors) { - stack = []; + }); + + if (!deferred.promise.isRejected()) { + this.client.bulk(data) + .then(function (result) { + var + responseObject, + stack; + + responseObject = new ResponseObject(requestObject, result); + // If some errors occured during the Bulk, we send a "Partial Error" response : + if (result.errors) { + stack = []; + async.each(result.items, function(resultItem, resultCallback) { + async.each(Object.keys(resultItem), function(action, callback) { + var item = resultItem[action]; + if (item.error) { + item.action = action; + stack.push(item); + } + callback(); + }); + resultCallback(); + }); + responseObject.error = new PartialError('Some errors on bulk', stack); + responseObject.status = responseObject.error.status; + } + async.each(result.items, function(resultItem, resultCallback) { async.each(Object.keys(resultItem), function(action, callback) { var item = resultItem[action]; - if (item.error) { - item.action = action; - stack.push(item); + if (action === 'index' && !item.error) { + indexCache.add.call(kuzzle, item._index, item._type); } callback(); }); resultCallback(); }); - responseObject.error = new PartialError('Some errors on bulk', stack); - responseObject.status = responseObject.error.status; - } - deferred.resolve(responseObject); - }) - .catch(function (error) { - deferred.reject(error); - }); + + deferred.resolve(responseObject); + }) + .catch(function (error) { + deferred.reject(error); + }); + } return deferred.promise; } @@ -373,9 +456,19 @@ module.exports = function (kuzzle, options) { * @return {Promise} */ this.putMapping = function (requestObject) { - var data = cleanData.call(this, requestObject); + var + deferred = q.defer(), + data = cleanData.call(this, requestObject); - return this.client.indices.putMapping(data); + this.client.indices.putMapping(data) + .then(function (result) { + deferred.resolve(new ResponseObject(requestObject, result)); + }) + .catch(function (error) { + deferred.reject(error); + }); + + return deferred.promise; }; /** @@ -392,14 +485,14 @@ module.exports = function (kuzzle, options) { delete data.body; this.client.indices.getMapping(data) - .then(function (result) { - if (result[this.kuzzleConfig[this.engineType].index]) { + .then(result => { + if (result[requestObject.index]) { deferred.resolve(new ResponseObject(requestObject, result)); } else { - deferred.reject(new NotFoundError('No mapping for current index')); + deferred.reject(new NotFoundError('No mapping for index "' + requestObject.index + '"')); } - }.bind(this)) + }) .catch(function (error) { deferred.reject(error); }); @@ -424,8 +517,8 @@ module.exports = function (kuzzle, options) { .then(result => { var collections = []; - if (result[this.kuzzleConfig[this.engineType].index]) { - collections = Object.keys(result[this.kuzzleConfig[this.engineType].index].mappings); + if (result[requestObject.index]) { + collections = Object.keys(result[requestObject.index].mappings); } deferred.resolve(new ResponseObject(requestObject, {collections: collections})); @@ -436,27 +529,80 @@ module.exports = function (kuzzle, options) { }; /** - * Reset the datastorage + * Reset all indexes * * @return {Promise} */ - this.reset = function () { + this.deleteIndexes = function (requestObject) { var deferred = q.defer(); - this.client.indices.delete({index: '_all'}) - .then(function () { - this.client.indices.create({index: 'mainindex'}) - .then(function () { - deferred.resolve(true); - }) - .catch(function (error) { - deferred.reject(error); - }); - }.bind(this)) - .catch(function (error) { - deferred.reject(error); - }); + delete requestObject.data.body; + + this.listIndexes(requestObject) + .then(result => { + if (result.data.indexes.length === 0) { + return Promise.resolve(); + } + + return this.client.indices.delete({index: result.data.indexes}); + }) + .then(() => { + indexCache.reset.call(kuzzle); + deferred.resolve(new ResponseObject(requestObject, {})); + }) + .catch(error => deferred.reject(error)); + + return deferred.promise; + }; + + this.listIndexes = function (requestObject) { + var + indexes = [], + deferred = q.defer(); + + this.client.indices.getMapping() + .then(result => { + indexes = Object.keys(result); + indexes = indexes.filter(indexName => { + // @todo : manage internal index properly + // exclude empty result and internal index + return indexName !== '' && indexName !== '%kuzzle'; + }); + + deferred.resolve(new ResponseObject(requestObject, {indexes: indexes})); + }) + .catch(error => deferred.reject(error)); + + return deferred.promise; + }; + + this.createIndex = function (requestObject) { + var + deferred = q.defer(), + data = cleanData.call(this, requestObject); + + this.client.indices.create({index: data.index}) + .then(result => { + indexCache.add.call(kuzzle, data.index); + deferred.resolve(new ResponseObject(requestObject, result)); + }) + .catch(error => deferred.reject(error)); + + return deferred.promise; + }; + + this.deleteIndex = function (requestObject) { + var + deferred = q.defer(), + data = cleanData.call(this, requestObject); + + this.client.indices.delete({index: data.index}) + .then(result => { + indexCache.remove.call(kuzzle, data.index); + deferred.resolve(new ResponseObject(requestObject, result)); + }) + .catch(error => deferred.reject(error)); return deferred.promise; }; @@ -464,13 +610,17 @@ module.exports = function (kuzzle, options) { /** * Clean requestObject data: remove all attributes created for kuzzle, - * add index if not defined and map the name 'collection' to 'type' for ES + * add index and map the name 'collection' to 'type' for ES * @param {RequestObject} requestObject * @return {Object} data the data with cleaned attributes */ function cleanData(requestObject) { var data = {}; + if (requestObject.index !== undefined) { + data.index = requestObject.index; + } + if (requestObject.collection !== undefined) { data.type = requestObject.collection; } @@ -485,9 +635,6 @@ function cleanData(requestObject) { } }); - // TODO: implement multi index - data.index = this.kuzzleConfig[this.engineType].index; - return data; } @@ -515,7 +662,7 @@ function getAllIdsFromQuery(data) { if (response.hits.total !== ids.length) { this.client.scroll({ scrollId: response._scroll_id, - scroll: '30s' + scroll: data.scroll }, getMoreUntilDone.bind(this)); } else { @@ -526,3 +673,37 @@ function getAllIdsFromQuery(data) { return deferred.promise; } + +indexCache = { + add: function(index, collection) { + if (index !== undefined) { + if (this.indexes[index] === undefined) { + this.indexes[index] = []; + } + + if (collection !== undefined) { + if (this.indexes[index].indexOf(collection) === -1) { + this.indexes[index].push(collection); + } + } + } + }, + remove: function(index, collection) { + if (index !== undefined) { + if (collection !== undefined) { + this.indexes[index].splice(this.indexes[index].indexOf(collection)); + } + else { + delete this.indexes[index]; + } + } + }, + reset: function(index) { + if (index !== undefined) { + this.indexes[index] = []; + } + else { + this.indexes = {}; + } + } +}; \ No newline at end of file diff --git a/lib/services/logstash.js b/lib/services/logstash.js index ed7fc0e4f4..98c6d8a2ef 100644 --- a/lib/services/logstash.js +++ b/lib/services/logstash.js @@ -51,7 +51,6 @@ module.exports = function () { message: { hookEvent: hookEvent, duration : log.duration, - testingParam: log.testingParam, processData: this.getProcessData(), timestamp: Date.now(), metaData: metaData diff --git a/lib/workers/write.js b/lib/workers/write.js index b7786b2326..56904d9ede 100644 --- a/lib/workers/write.js +++ b/lib/workers/write.js @@ -35,8 +35,7 @@ function onListenCB (serializedRequestObject) { this.emit('worker:write:' + serializedRequestObject.protocol + ':start', serializedRequestObject); this.services.list.writeEngine[serializedRequestObject.action](serializedRequestObject) - .then(function (result) { - var responseObject = new ResponseObject(serializedRequestObject, result); + .then(responseObject => { this.emit('worker:write:' + serializedRequestObject.protocol + ':stop', responseObject); @@ -45,9 +44,9 @@ function onListenCB (serializedRequestObject) { // notify rooms about the created/updated/deleted document this.services.list.broker.add(this.config.queues.coreNotifierTaskQueue, responseObject); - }.bind(this)) - .catch(function (error) { + }) + .catch(error => { var responseObject = new ResponseObject(serializedRequestObject, error); this.services.list.broker.add(this.config.queues.workerWriteResponseQueue, responseObject); - }.bind(this)); + }); } diff --git a/package.json b/package.json index de0b412e70..e1549e9ada 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "kuzzle": "./bin/kuzzle.js" }, "scripts": { - "test": "npm run unit-testing --coverage && npm run functional-testing && npm run crawl-coverage", + "test": "npm run unit-testing --coverage && npm run functional-testing && npm run crawl-coverage && grunt", "unit-testing": "istanbul test _mocha", "functional-testing": "npm run cucumber", "cucumber": "cucumber.js", diff --git a/test/api/controllers/adminController.test.js b/test/api/controllers/adminController.test.js index 45edbcca0a..08b4f54d95 100644 --- a/test/api/controllers/adminController.test.js +++ b/test/api/controllers/adminController.test.js @@ -4,15 +4,14 @@ var params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), RequestObject = require.main.require('lib/api/core/models/requestObject'), - ResponseObject = require.main.require('lib/api/core/models/responseObject'), - NotFoundError = require.main.require('lib/api/core/errors/notFoundError'); + ResponseObject = require.main.require('lib/api/core/models/responseObject'); require('should-promised'); describe('Test: admin controller', function () { var kuzzle, - requestObject = new RequestObject({ controller: 'admin' }, { collection: 'unit-test-adminController' }, 'unit-test'); + requestObject = new RequestObject({ controller: 'admin' }, { index: '%test', collection: 'unit-test-adminController' }, 'unit-test'); before(function (done) { kuzzle = new Kuzzle(); @@ -21,8 +20,8 @@ describe('Test: admin controller', function () { .then(function () { kuzzle.repositories.role.validateAndSaveRole = role => { return Promise.resolve({ - _index: 'mainindex', - _type: '%kuzzle/roles', + _index: '%kuzzle', + _type: 'roles', _id: role._id, created: true }); @@ -72,7 +71,7 @@ describe('Test: admin controller', function () { it('should return a mapping when requested', function () { var r = kuzzle.funnel.admin.getMapping(requestObject); - return should(r).be.rejectedWith(NotFoundError, { message: 'No mapping for current index' }); + return should(r).be.rejected(); }); it('should activate a hook on a get mapping call', function (done) { @@ -170,4 +169,52 @@ describe('Test: admin controller', function () { }); }); + it('should trigger a hook on a deleteIndexes call', function (done) { + this.timeout(50); + + kuzzle.once('data:deleteIndexes', function (obj) { + try { + should(obj).be.exactly(requestObject); + done(); + } + catch (e) { + done(e); + } + }); + + kuzzle.funnel.admin.deleteIndexes(requestObject); + }); + + it('should trigger a hook on a createIndex call', function (done) { + this.timeout(50); + + kuzzle.once('data:createIndex', function (obj) { + try { + should(obj).be.exactly(requestObject); + done(); + } + catch (e) { + done(e); + } + }); + + kuzzle.funnel.admin.createIndex(requestObject); + }); + + it('should trigger a hook on a deleteIndex call', function (done) { + this.timeout(50); + + kuzzle.once('data:deleteIndex', function (obj) { + try { + should(obj).be.exactly(requestObject); + done(); + } + catch (e) { + done(e); + } + }); + + kuzzle.funnel.admin.deleteIndex(requestObject); + }); + }); diff --git a/test/api/controllers/funnelController/execute.test.js b/test/api/controllers/funnelController/execute.test.js index 583b860a5f..8660a0344b 100644 --- a/test/api/controllers/funnelController/execute.test.js +++ b/test/api/controllers/funnelController/execute.test.js @@ -120,6 +120,7 @@ describe('Test execute function in funnel controller', function () { kuzzle.funnel.execute( new RequestObject({ controller: 'read', + index: '@test', action: 'get' }), localContext) @@ -130,6 +131,7 @@ describe('Test execute function in funnel controller', function () { var object = { requestId: 'requestId', controller: 'write', + index: '@test', action: 'create', collection: 'user', persist: false, diff --git a/test/api/controllers/readController.test.js b/test/api/controllers/readController.test.js index d28df2e185..cf9b86da05 100644 --- a/test/api/controllers/readController.test.js +++ b/test/api/controllers/readController.test.js @@ -3,6 +3,7 @@ var winston = require('winston'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), + ResponseObject = require.main.require('lib/api/core/models/responseObject'), RequestObject = require.main.require('lib/api/core/models/requestObject'); require('should-promised'); @@ -11,108 +12,89 @@ require('should-promised'); * Since we're querying the database for non-existent documents, we expect * most of these calls, if not all, to return a rejected promise. */ -describe('Test: read controller', function () { - var - kuzzle; - - before(function (done) { - kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); - kuzzle.start(params, {dummy: true}) - .then(function () { - done(); - }); - }); - - it('should allow to perform searches', function () { - var - requestObject = new RequestObject({}, {collection: 'unit-test-readcontroller'}, 'unit-test'), - r = kuzzle.funnel.read.search(requestObject); - - return should(r).be.rejectedWith(Error, { status: 400 }); - }); +var + kuzzle; + +before(function (done) { + kuzzle = new Kuzzle(); + kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); + kuzzle.start(params, {dummy: true}) + .then(function () { + kuzzle.services.list.readEngine = { + search: function(requestObject) { return Promise.resolve(new ResponseObject(requestObject, {})); }, + get: function(requestObject) { return Promise.resolve(new ResponseObject(requestObject, {})); }, + count: function(requestObject) { return Promise.resolve(new ResponseObject(requestObject, {})); }, + listCollections: function(requestObject) { return Promise.resolve(new ResponseObject(requestObject, {})); }, + listIndexes: function(requestObject) { return Promise.resolve(new ResponseObject(requestObject, {})); } + }; + done(); + }); +}); - it('should allow to get specific documents', function () { - var - requestObject = new RequestObject({ body: { _id: 'foobar' }}, { collection: 'unit-test-readcontroller' }, 'unit-test'), - r = kuzzle.funnel.read.get(requestObject); +describe('Test: read controller', function () { - return should(r).be.rejectedWith(Error, { status: 404 }); + after(function (done) { + done(); }); - it('should allow to count documents', function () { - var - requestObject = new RequestObject({}, {collection: 'unit-test-readcontroller'}, 'unit-test'), - r = kuzzle.funnel.read.count(requestObject); + it('should emit a data:search hook when searching', function (done) { + var requestObject = new RequestObject({index: '%test', collection: 'unit-test-readcontroller'}); - return should(r).be.rejectedWith(Error, { status: 400 }); + this.timeout(50); + kuzzle.once('data:search', () => done()); + kuzzle.funnel.read.search(requestObject); }); - it('should allow to list all existing colletions', function (done) { - var - requestObject = new RequestObject({}, {}, ''), - r = kuzzle.funnel.read.listCollections(requestObject); - - should(r).be.a.Promise(); + it('should emit a data:get hook when reading', function (done) { + var requestObject = new RequestObject({index: '%test', collection: 'unit-test-readcontroller'}); - r - .then(result => { - should(result.data.collections).not.be.undefined().and.be.an.Array(); - done(); - }) - .catch(error => done(error)); + this.timeout(50); + kuzzle.once('data:get', () => done()); + kuzzle.funnel.read.get(requestObject); }); - it('should trigger a plugin event when performing searches', function (done) { - var requestObject = new RequestObject({}, {collection: 'unit-test-readcontroller'}, 'unit-test'); + it('should emit a data:count hook when counting', function (done) { + var requestObject = new RequestObject({index: '%test', collection: 'unit-test-readcontroller'}); this.timeout(50); - kuzzle.on('data:search', () => done()); - kuzzle.funnel.read.search(requestObject); + kuzzle.once('data:count', () => done()); + kuzzle.funnel.read.count(requestObject); }); - it('should trigger a plugin event when getting specific documents', function (done) { - var requestObject = new RequestObject({ body: { _id: 'foobar' }}, { collection: 'unit-test-readcontroller' }, 'unit-test'); + it('should emit a data:listCollections hook when reading collections', function (done) { + var requestObject = new RequestObject({index: '%test', collection: 'unit-test-readcontroller'}); this.timeout(50); - kuzzle.on('data:get', () => done()); - kuzzle.funnel.read.get(requestObject); + kuzzle.once('data:listCollections', () => done()); + kuzzle.funnel.read.listCollections(requestObject); }); - it('should trigger a plugin event when counting documents', function (done) { - var requestObject = new RequestObject({}, {collection: 'unit-test-readcontroller'}, 'unit-test'); + it('should emit a data:now hook when reading kuzzle time', function (done) { + var requestObject = new RequestObject({index: '%test', collection: 'unit-test-readcontroller'}); this.timeout(50); - kuzzle.on('data:count', () => done()); - kuzzle.funnel.read.count(requestObject); + kuzzle.once('data:now', () => done()); + kuzzle.funnel.read.now(requestObject); }); - it('should trigger a plugin event when listing all existing collections', function (done) { - var requestObject = new RequestObject({}, {}, ''); + it('should emit a data:listIndexes hook when reading indexes', function (done) { + var requestObject = new RequestObject({index: '%test', collection: 'unit-test-readcontroller'}); this.timeout(50); - kuzzle.on('data:listCollections', () => done()); - kuzzle.funnel.read.listCollections(requestObject); + kuzzle.once('data:listIndexes', () => done()); + kuzzle.funnel.read.listIndexes(requestObject); }); - it('should resolve to the current timestamp when calling the read/now API route', function () { + it('should retrieve a number when requesting server time', function () { var - requestObject = new RequestObject({}, {}, ''), - result = kuzzle.funnel.read.now(requestObject); + requestObject = new RequestObject({}), + promisedResult = kuzzle.funnel.read.now(requestObject); - should(result).be.a.Promise(); + should(promisedResult).be.a.Promise(); - return result.then(result => { + return promisedResult.then(result => { should(result.data).not.be.undefined(); should(result.data.now).not.be.undefined().and.be.a.Number(); }); }); - - it('should trigger a plugin event when getting the current timestamp', function (done) { - var requestObject = new RequestObject({}, {}, ''); - - this.timeout(50); - kuzzle.on('data:now', () => done()); - kuzzle.funnel.read.now(requestObject); - }); }); diff --git a/test/api/controllers/routerController/executeFromRest.test.js b/test/api/controllers/routerController/executeFromRest.test.js index c9c9ae623a..f3f0a09b56 100644 --- a/test/api/controllers/routerController/executeFromRest.test.js +++ b/test/api/controllers/routerController/executeFromRest.test.js @@ -75,10 +75,10 @@ describe('Test: routerController.executeFromRest', function () { }); it('should reject requests when the controller is not provided', function () { - var params = { action: 'create', collection: 'foobar' }; + var callParams = { action: 'create' }; mockupResponse.init(); - executeFromRest.call(kuzzle, params, {headers: {'content-type': 'application/json'}}, mockupResponse); + executeFromRest.call(kuzzle, callParams, {headers: {'content-type': 'application/json'}, params: { collection: 'foobar', index: '%test'}}, mockupResponse); should(mockupResponse.statusCode).be.exactly(400); should(mockupResponse.header['Content-Type']).not.be.undefined(); @@ -93,7 +93,7 @@ describe('Test: routerController.executeFromRest', function () { it('should reject requests when the content-type is not application/json', function () { var params = { action: 'create', controller: 'write' }, - data = {_body: true, headers: {'content-type': '"application/x-www-form-urlencoded'}, body: {resolve: true}, params: {collection: 'foobar'}}; + data = {_body: true, headers: {'content-type': '"application/x-www-form-urlencoded'}, body: {resolve: true}, params: {collection: 'foobar', index: '%test'}}; mockupResponse.init(); executeFromRest.call(kuzzle, params, data, mockupResponse); @@ -110,7 +110,7 @@ describe('Test: routerController.executeFromRest', function () { it('should respond with a HTTP 200 message in case of success', function (done) { var params = { action: 'create', controller: 'write' }, - data = {headers: {'content-type': 'application/json'}, body: {resolve: true}, params: {collection: 'foobar'}}; + data = {headers: {'content-type': 'application/json'}, body: {resolve: true}, params: {index: '%test', collection: 'foobar'}}; mockupResponse.init(); executeFromRest.call(kuzzle, params, data, mockupResponse); @@ -151,7 +151,7 @@ describe('Test: routerController.executeFromRest', function () { body: { resolve: true }, - params: {collection: 'foobar'} + params: {index: '%test', ollection: 'foobar'} }; mockupResponse.init(); @@ -188,11 +188,11 @@ describe('Test: routerController.executeFromRest', function () { it('should not respond if the response is empty', function (done) { var - params = { action: 'create', controller: 'write' }, - data = {headers: {'content-type': 'application/json'}, body: {resolve: true, empty: true}, params: {collection: 'foobar'}}; + callParams = { action: 'create', controller: 'write' }, + data = {headers: {'content-type': 'application/json'}, body: {resolve: true, empty: true}, params: {index: '%test', collection: 'foobar'}}; mockupResponse.init(); - executeFromRest.call(kuzzle, params, data, mockupResponse); + executeFromRest.call(kuzzle, callParams, data, mockupResponse); setTimeout(function () { try { @@ -207,11 +207,11 @@ describe('Test: routerController.executeFromRest', function () { it('should respond with a HTTP 500 message in case of error', function (done) { var - params = { action: 'create', controller: 'write' }, - data = {headers: {'content-type': 'application/json'}, body: {resolve: false}, params: {collection: 'foobar'}}; + callParams = { action: 'create', controller: 'write' }, + data = {headers: {'content-type': 'application/json'}, body: {resolve: false}, params: {index: '%test', collection: 'foobar'}}; mockupResponse.init(); - executeFromRest.call(kuzzle, params, data, mockupResponse); + executeFromRest.call(kuzzle, callParams, data, mockupResponse); setTimeout(function () { try { @@ -233,11 +233,11 @@ describe('Test: routerController.executeFromRest', function () { it('should use the request content instead of the metadata to complete missing information', function (done) { var - params = {controller: 'write' }, - data = {headers: {'content-type': 'application/json'}, body: {resolve: true}, params: {collection: 'foobar', action: 'create'}}; + callParams = {controller: 'write' }, + data = {headers: {'content-type': 'application/json'}, body: {resolve: true}, params: {index: '%test', collection: 'foobar', action: 'create'}}; mockupResponse.init(); - executeFromRest.call(kuzzle, params, data, mockupResponse); + executeFromRest.call(kuzzle, callParams, data, mockupResponse); setTimeout(function () { try { @@ -259,11 +259,11 @@ describe('Test: routerController.executeFromRest', function () { it('should copy any found "id" identifier', function (done) { var - params = {controller: 'write' }, - data = {headers: {'content-type': 'application/json'}, body: {resolve: true}, params: {collection: 'foobar', action: 'create', id: 'fakeid'}}; + callParams = {controller: 'write' }, + data = {headers: {'content-type': 'application/json'}, body: {resolve: true}, params: {index: '%test', collection: 'foobar', action: 'create', id: 'fakeid'}}; mockupResponse.init(); - executeFromRest.call(kuzzle, params, data, mockupResponse); + executeFromRest.call(kuzzle, callParams, data, mockupResponse); setTimeout(function () { try { diff --git a/test/api/controllers/routerController/initRouterHttp.test.js b/test/api/controllers/routerController/initRouterHttp.test.js index 3a82578dbb..667f29399a 100644 --- a/test/api/controllers/routerController/initRouterHttp.test.js +++ b/test/api/controllers/routerController/initRouterHttp.test.js @@ -104,7 +104,7 @@ describe('Test: routerController.initRouterHttp', function () { }); it('should reply with a Hello World on a simple GET query', function (done) { - http.get('http://' + options.hostname + ':' + options.port + '/api', function (response) { + http.get('http://' + options.hostname + ':' + options.port + '/api/v1.0', function (response) { parseHttpResponse(response) .then(function (result) { should(result.status).be.exactly(200); @@ -122,7 +122,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'POST'; - options.path= '/api/collection'; + options.path= '/api/v1.0/index/collection'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -145,7 +145,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'POST'; - options.path= '/api/collection/_create'; + options.path= '/api/v1.0/index/collection/_create'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -165,7 +165,7 @@ describe('Test: routerController.initRouterHttp', function () { }); it('should create a route for document retrieving', function (done) { - http.get('http://' + options.hostname + ':' + options.port + '/api/collection/documentID', function (response) { + http.get('http://' + options.hostname + ':' + options.port + '/api/v1.0/index/collection/documentID', function (response) { parseHttpResponse(response) .then(function (result) { should(response.statusCode).be.exactly(200); @@ -183,7 +183,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'POST'; - options.path= '/api/collection/_search'; + options.path= '/api/v1.0/index/collection/_search'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -206,7 +206,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'PUT'; - options.path= '/api/collection/documentID'; + options.path= '/api/v1.0/index/collection/documentID'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -229,7 +229,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'PUT'; - options.path= '/api/collection/documentID/_update'; + options.path= '/api/v1.0/index/collection/documentID/_update'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -252,7 +252,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'POST'; - options.path= '/api/collection/_count'; + options.path= '/api/v1.0/index/collection/_count'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -275,7 +275,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'DELETE'; - options.path= '/api/collection/documentID'; + options.path= '/api/v1.0/index/collection/documentID'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -298,7 +298,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'DELETE'; - options.path= '/api/collection/_query'; + options.path= '/api/v1.0/index/collection/_query'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -321,7 +321,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'DELETE'; - options.path= '/api/collection'; + options.path= '/api/v1.0/index/collection'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -344,7 +344,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'PUT'; - options.path= '/api/collection/_mapping'; + options.path= '/api/v1.0/index/collection/_mapping'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -367,7 +367,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'GET'; - options.path= '/api/collection/_mapping'; + options.path= '/api/v1.0/index/collection/_mapping'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -390,7 +390,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'POST'; - options.path= '/api/collection/_bulk'; + options.path= '/api/v1.0/index/collection/_bulk'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -413,7 +413,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'POST'; - options.path= '/api/_bulk'; + options.path= '/api/v1.0/index/_bulk'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -436,7 +436,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'PUT'; - options.path= '/api/collection/documentID/_delete'; + options.path= '/api/v1.0/index/collection/documentID/_delete'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -459,7 +459,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'PUT'; - options.path= '/api/collection/documentID/_create'; + options.path= '/api/v1.0/index/collection/documentID/_create'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -482,7 +482,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'PUT'; - options.path= '/api/collection/documentID/_createOrUpdate'; + options.path= '/api/v1.0/index/collection/documentID/_createOrUpdate'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -543,7 +543,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'POST'; - options.path= '/api/_getStats'; + options.path= '/api/v1.0/_getStats'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -567,7 +567,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'GET'; - options.path= '/api/_getAllStats'; + options.path= '/api/v1.0/_getAllStats'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -587,7 +587,7 @@ describe('Test: routerController.initRouterHttp', function () { }); it('should create a route for the listCollection command', function (done) { - http.get('http://' + options.hostname + ':' + options.port + '/api/_listCollections', function (response) { + http.get('http://' + options.hostname + ':' + options.port + '/api/v1.0/index/_listCollections', function (response) { parseHttpResponse(response) .then(result => { should(response.statusCode).be.exactly(200); @@ -601,7 +601,7 @@ describe('Test: routerController.initRouterHttp', function () { it('should create a route for the now command', function (done) { - http.get('http://' + options.hostname + ':' + options.port + '/api/_now', function (response) { + http.get('http://' + options.hostname + ':' + options.port + '/api/v1.0/_now', function (response) { parseHttpResponse(response) .then(result => { should(response.statusCode).be.exactly(200); @@ -617,7 +617,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'PUT'; - options.path= '/api/collection'; + options.path= '/api/v1.0/index/collection'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -638,7 +638,7 @@ describe('Test: routerController.initRouterHttp', function () { var request; options.method = 'DELETE'; - options.path= '/api/collection/_truncate'; + options.path= '/api/v1.0/index/collection/_truncate'; request = http.request(options, function (response) { parseHttpResponse(response) @@ -655,8 +655,92 @@ describe('Test: routerController.initRouterHttp', function () { request.end(); }); + it('should create a route for the listIndexes command', done => { + var request; + + options.method = 'GET'; + options.path= '/api/v1.0/_listIndexes'; + + request = http.request(options, function (response) { + parseHttpResponse(response) + .then(result => { + should(response.statusCode).be.exactly(200); + should(result.controller).be.exactly('read'); + should(result.action).be.exactly('listIndexes'); + done(); + }) + .catch(error => done(error)); + }); + + request.write(''); + request.end(); + }); + + it('should create a route for the createIndex command', done => { + var request; + + options.method = 'PUT'; + options.path= '/api/v1.0/index'; + + request = http.request(options, function (response) { + parseHttpResponse(response) + .then(result => { + should(response.statusCode).be.exactly(200); + should(result.controller).be.exactly('admin'); + should(result.action).be.exactly('createIndex'); + done(); + }) + .catch(error => done(error)); + }); + + request.write('foobar'); + request.end(); + }); + + it('should create a route for the deleteIndex command', done => { + var request; + + options.method = 'DELETE'; + options.path= '/api/v1.0/index'; + + request = http.request(options, function (response) { + parseHttpResponse(response) + .then(result => { + should(response.statusCode).be.exactly(200); + should(result.controller).be.exactly('admin'); + should(result.action).be.exactly('deleteIndex'); + done(); + }) + .catch(error => done(error)); + }); + + request.write('foobar'); + request.end(); + }); + + it('should create a route for the deleteIndexes command', done => { + var request; + + options.method = 'DELETE'; + options.path= '/api/v1.0/_deleteIndexes'; + + request = http.request(options, function (response) { + parseHttpResponse(response) + .then(result => { + should(response.statusCode).be.exactly(200); + should(result.controller).be.exactly('admin'); + should(result.action).be.exactly('deleteIndexes'); + done(); + }) + .catch(error => done(error)); + }); + + request.write('foobar'); + request.end(); + }); + it('should create a GET route for plugin controller', function (done) { - http.get('http://' + options.hostname + ':' + options.port + '/api/_plugin/myplugin/bar/name', function (response) { + http.get('http://' + options.hostname + ':' + options.port + '/api/v1.0/_plugin/myplugin/bar/name', function (response) { parseHttpResponse(response) .then(function (result) { should(response.statusCode).be.exactly(200); @@ -672,7 +756,7 @@ describe('Test: routerController.initRouterHttp', function () { it('should create a POST route for plugin controller', function (done) { options.method = 'POST'; - options.path= '/api/_plugin/myplugin/bar'; + options.path= '/api/v1.0/_plugin/myplugin/bar'; request = http.request(options, function (response) { parseHttpResponse(response) diff --git a/test/api/controllers/subscribeController.test.js b/test/api/controllers/subscribeController.test.js index 8a881f18d7..ff08ede5bd 100644 --- a/test/api/controllers/subscribeController.test.js +++ b/test/api/controllers/subscribeController.test.js @@ -20,7 +20,7 @@ describe('Test: subscribe controller', function () { kuzzle, anonymousUser, context, - requestObject = new RequestObject({}, {}, 'unit-test'); + requestObject = new RequestObject({index: 'test'}, {}, 'unit-test'); before(function (done) { context = {}; @@ -44,14 +44,13 @@ describe('Test: subscribe controller', function () { }); }); - beforeEach(() => requestObject = new RequestObject({controller: 'subscribe'}, {}, 'unit-test')); + beforeEach(() => requestObject = new RequestObject({index: 'test', collection: 'collection', controller: 'subscribe'}, {}, 'unit-test')); it('should forward new subscriptions to the hotelClerk core component', function () { var foo = kuzzle.funnel.subscribe.on(requestObject, { - connection: {id: 'foobar'}, - user: anonymousUser - } - ); + connection: {id: 'foobar'}, + user: anonymousUser + }); return should(foo).be.fulfilled(); }); @@ -61,12 +60,11 @@ describe('Test: subscribe controller', function () { newUser = 'Carmen Sandiego', result; - requestObject.data.body = { roomId: 'foobar' }; - result = kuzzle.funnel.subscribe.off(requestObject, { - connection: {id: newUser }, - user: anonymousUser - } - ); + requestObject.data.body = { roomId: 'foobar' }; + result = kuzzle.funnel.subscribe.off(requestObject, { + connection: {id: newUser }, + user: anonymousUser + }); return should(result).be.rejectedWith(NotFoundError, { message: 'The user with connection ' + newUser + ' doesn\'t exist' }); }); diff --git a/test/api/controllers/writeController.test.js b/test/api/controllers/writeController.test.js index dab167c46c..dbee9aa17e 100644 --- a/test/api/controllers/writeController.test.js +++ b/test/api/controllers/writeController.test.js @@ -20,29 +20,26 @@ describe('Test: write controller', function () { kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.start(params, {dummy: true}) .then(function () { + kuzzle.services.list.writeEngine = {}; done(); }); }); - it('should reject an empty request', function () { - var requestObject = new RequestObject({}, {}, 'unit-test'); + it('should reject an empty request', function (done) { + var requestObject = new RequestObject({}); delete requestObject.data.body; - return should(kuzzle.funnel.write.create(requestObject)).be.rejected() - .then(function () { - return should(kuzzle.funnel.write.update(requestObject)).be.rejected(); - }) - .then(function () { - return should(kuzzle.funnel.write.createOrUpdate(requestObject)).be.rejected(); - }) - .then(function () { - return should(kuzzle.funnel.write.publish(requestObject)).be.rejected(); - }); + should(requestObject.isValid()).be.rejected(); + should(kuzzle.funnel.write.create(requestObject)).be.rejected(); + should(kuzzle.funnel.write.createOrUpdate(requestObject)).be.rejected(); + should(kuzzle.funnel.write.update(requestObject)).be.rejected(); + done(); }); describe('#create', function () { it('should emit a hook on a create data query', function (done) { - var requestObject = new RequestObject({body: {foo: 'bar'}}, {}, 'unit-test'); + var + requestObject = new RequestObject({index: 'test', body: {foo: 'bar'}, persist: false}, {}, 'unit-test'); this.timeout(50); @@ -64,7 +61,7 @@ describe('Test: write controller', function () { it('should not send notifications right away when creating persistent messages', function (done) { var - requestObject = new RequestObject({body: {foo: 'bar'}, persist: true}, {}, 'unit-test'), + requestObject = new RequestObject({index: 'test', body: {foo: 'bar'}, persist: true}, {}, 'unit-test'), created; kuzzle.notifier.notify = function () { @@ -84,7 +81,7 @@ describe('Test: write controller', function () { it('should send notifications when publishing messages', function (done) { var mockupRooms = ['foo', 'bar'], - requestObject = new RequestObject({body: {foo: 'bar'}}, {}, 'unit-test'); + requestObject = new RequestObject({index: 'test', body: {foo: 'bar'}}, {}, 'unit-test'); this.timeout(50); @@ -207,6 +204,7 @@ describe('Test: write controller', function () { }); }); + describe('#createCollection', function () { it('should trigger a hook on a createCollection call', function (done) { var requestObject = new RequestObject({}, {}, 'unit-test'); diff --git a/test/api/core/hotelClerck/addSubscription.test.js b/test/api/core/hotelClerck/addSubscription.test.js index c1499ff9c4..4613d4021b 100644 --- a/test/api/core/hotelClerck/addSubscription.test.js +++ b/test/api/core/hotelClerck/addSubscription.test.js @@ -23,6 +23,7 @@ describe('Test: hotelClerk.addSubscription', function () { user: null }, roomName = 'roomName', + index = 'test', collection = 'user', filter = { term: { @@ -70,6 +71,7 @@ describe('Test: hotelClerk.addSubscription', function () { controller: 'subscribe', action: 'on', requestId: roomName, + index: index, collection: collection, body: filter, metadata: { @@ -119,6 +121,7 @@ describe('Test: hotelClerk.addSubscription', function () { requestObject = new RequestObject({ controller: 'subscribe', collection: collection, + index: index, body: filter }); @@ -148,6 +151,7 @@ describe('Test: hotelClerk.addSubscription', function () { var requestObject = new RequestObject({ controller: 'subscribe', collection: collection, + index: index, body: filter }); var response; @@ -170,6 +174,7 @@ describe('Test: hotelClerk.addSubscription', function () { controller: 'subscribe', action: 'on', collection: collection, + index: index, body: {badterm : {firstName: 'Ada'}} }); @@ -182,6 +187,7 @@ describe('Test: hotelClerk.addSubscription', function () { requestObject1 = new RequestObject({ controller: 'subscribe', collection: collection, + index: index, body: { term: { firstName: 'Ada' @@ -194,6 +200,7 @@ describe('Test: hotelClerk.addSubscription', function () { requestObject2 = new RequestObject({ controller: 'subscribe', collection: collection, + index: index, body: { exists: { field: 'lastName' @@ -223,6 +230,7 @@ describe('Test: hotelClerk.addSubscription', function () { var requestObject = new RequestObject({ controller: 'subscribe', + index: index, collection: collection }); @@ -235,6 +243,7 @@ describe('Test: hotelClerk.addSubscription', function () { var requestObject = new RequestObject({ controller: 'subscribe', + index: index, collection: collection }); @@ -262,6 +271,7 @@ describe('Test: hotelClerk.addSubscription', function () { roomId, requestObject1 = new RequestObject({ controller: 'subscribe', + index: index, collection: collection }); @@ -275,6 +285,7 @@ describe('Test: hotelClerk.addSubscription', function () { .then(id => { var requestObject2 = new RequestObject({ collection: collection, + index: index, controller: 'subscribe', action: 'join', body: { @@ -301,6 +312,7 @@ describe('Test: hotelClerk.addSubscription', function () { return should(kuzzle.hotelClerk.join( new RequestObject({ collection: collection, + index: index, controller: 'subscribe', action: 'join', body: {roomId: 'no way I can exist'} diff --git a/test/api/core/hotelClerck/countSubscription.test.js b/test/api/core/hotelClerck/countSubscription.test.js index f10e842da4..6bc69386fe 100644 --- a/test/api/core/hotelClerck/countSubscription.test.js +++ b/test/api/core/hotelClerck/countSubscription.test.js @@ -58,12 +58,13 @@ describe('Test: hotelClerk.countSubscription', function () { aContext, anotherContext, subscribeRequest = new RequestObject({ - controller: 'subscribe', - action: 'on', - requestId: 'foo', - collection: 'bar', - body: { term: { foo: 'bar' } } - }), + controller: 'subscribe', + action: 'on', + requestId: 'foo', + collection: 'bar', + index: 'test', + body: { term: { foo: 'bar' } } + }), countRequest = new RequestObject({ body: {}}); aContext = { diff --git a/test/api/core/hotelClerck/listSubscriptions.test.js b/test/api/core/hotelClerck/listSubscriptions.test.js index 4fbc0d2aec..79e51a9967 100644 --- a/test/api/core/hotelClerck/listSubscriptions.test.js +++ b/test/api/core/hotelClerck/listSubscriptions.test.js @@ -10,15 +10,17 @@ var require('should-promised'); -describe('Test: hotelClerk.listSubscriptions', function () { +describe('Test: hotelClerk.addSubscription', function () { var kuzzle, + roomId, connection = {id: 'connectionid'}, context = { connection: connection, user: null }, roomName = 'roomName', + index = '%test', collection = 'user', filter = { term: { @@ -65,13 +67,16 @@ describe('Test: hotelClerk.listSubscriptions', function () { }); it('should return a correct list according to subscribe on filter', function () { - var requestObject = new RequestObject({ - controller: 'subscribe', - action: 'on', - requestId: roomName, - collection: collection, - body: filter - }); + var + roomName = 'd0d7627d6fedf3b8719a1602032f7117', + requestObject = new RequestObject({ + controller: 'subscribe', + action: 'on', + requestId: roomName, + index: index, + collection: collection, + body: filter + }); return kuzzle.hotelClerk.addSubscription(requestObject, context) .then(() => { @@ -82,20 +87,26 @@ describe('Test: hotelClerk.listSubscriptions', function () { should(responseObject).have.property('data'); should(responseObject.data).have.property('body'); // user -> collection - should(responseObject.data.body).have.property('user'); + should(responseObject.data.body).have.property(index); + should(responseObject.data.body[index]).have.property(collection); + + // there is no subscribe on whole collection + should(responseObject.data.body[index][collection]).not.have.property('totalGlobals'); // 3e0e837b447bf16b2251025ad36f39ed -> room id generated with collection and filter - should(responseObject.data.body.user).have.property('3e0e837b447bf16b2251025ad36f39ed'); - should(responseObject.data.body.user['3e0e837b447bf16b2251025ad36f39ed']).be.equal(1); + should(responseObject.data.body[index][collection]).have.property(roomName); + should(responseObject.data.body[index][collection][roomName]).be.equal(1); }); }); - it('should return a correct list according to subscribe on filter and user right', function () { + it('should return a correct list according to subscribe on filter and user right', function () { var + roomName = 'd0d7627d6fedf3b8719a1602032f7117', requestObjectUser = new RequestObject({ controller: 'subscribe', action: 'on', requestId: roomName, + index: index, collection: collection, body: filter }), @@ -103,12 +114,14 @@ describe('Test: hotelClerk.listSubscriptions', function () { controller: 'subscribe', action: 'on', requestId: roomName, + index: index, collection: 'foo', body: filter }), requestObjectList = new RequestObject({ controller: 'subscribe', action: 'list', + index: index, requestId: roomName, body: {} }); @@ -130,22 +143,24 @@ describe('Test: hotelClerk.listSubscriptions', function () { should(responseObject).have.property('data'); should(responseObject.data).have.property('body'); // user -> collection - should(responseObject.data.body).have.property('user'); + should(responseObject.data.body).have.property(index); + should(responseObject.data.body[index]).have.property(collection); // 3e0e837b447bf16b2251025ad36f39ed -> room id generated with collection and filter - should(responseObject.data.body.user).have.property('3e0e837b447bf16b2251025ad36f39ed'); - should(responseObject.data.body.user['3e0e837b447bf16b2251025ad36f39ed']).be.equal(1); + should(responseObject.data.body[index][collection]).have.property(roomName); + should(responseObject.data.body[index][collection][roomName]).be.equal(1); // should not return the collection foo - should(responseObject.data.body).not.have.property('foo'); + should(responseObject.data.body[index]).not.have.property('foo'); }); }); - it('should return a correct list according to subscribe on whole collection', function () { + it('should return a correct list according to subscribe on whole collection', function () { var requestObject = new RequestObject({ controller: 'subscribe', action: 'on', requestId: roomName, + index: index, collection: collection, body: {} }); @@ -158,8 +173,8 @@ describe('Test: hotelClerk.listSubscriptions', function () { .then(responseObject => { should(responseObject).have.property('data'); should(responseObject.data).have.property('body'); - // user -> collection - should(responseObject.data.body).have.property('user'); + should(responseObject.data.body).have.property(index); + should(responseObject.data.body[index]).have.property(collection); }); }); -}); +}); \ No newline at end of file diff --git a/test/api/core/hotelClerck/removeAllRooms.test.js b/test/api/core/hotelClerck/removeAllRooms.test.js index 632fb39a90..03722bc51c 100644 --- a/test/api/core/hotelClerck/removeAllRooms.test.js +++ b/test/api/core/hotelClerck/removeAllRooms.test.js @@ -20,6 +20,7 @@ describe('Test: hotelClerk.removeRooms', function () { user: null }, roomName = 'roomName', + index = 'test', collection1 = 'user', collection2 = 'foo', filter1 = { @@ -57,11 +58,25 @@ describe('Test: hotelClerk.removeRooms', function () { }); }); + it('should reject an error if no index provided', function () { + var requestObject = new RequestObject({ + controller: 'admin', + action: 'removeRooms', + requestId: roomName, + index: undefined, + collection: collection1, + body: {} + }); + + return should(kuzzle.hotelClerk.removeRooms(requestObject)).rejectedWith(BadRequestError); + }); + it('should reject an error if no collection provided', function () { var requestObject = new RequestObject({ controller: 'admin', action: 'removeRooms', requestId: roomName, + index: index, collection: undefined, body: {} }); @@ -69,11 +84,12 @@ describe('Test: hotelClerk.removeRooms', function () { return should(kuzzle.hotelClerk.removeRooms(requestObject)).rejectedWith(BadRequestError); }); - it('should reject an error if there is no subscription on this collection', function () { + it('should reject an error if there is no subscription on this index', function () { var requestObject = new RequestObject({ controller: 'admin', action: 'removeRooms', requestId: roomName, + index: index, collection: collection1, body: {} }); @@ -81,12 +97,39 @@ describe('Test: hotelClerk.removeRooms', function () { return should(kuzzle.hotelClerk.removeRooms(requestObject)).rejectedWith(NotFoundError); }); + it('should reject an error if there is no subscription on this collection', function () { + + var + requestObjectSubscribe = new RequestObject({ + controller: 'subscribe', + action: 'on', + requestId: roomName, + index: index, + collection: collection1, + body: {} + }), + requestObject = new RequestObject({ + controller: 'admin', + action: 'removeRooms', + requestId: roomName, + index: index, + collection: collection2, + body: {} + }); + + return kuzzle.hotelClerk.addSubscription(requestObjectSubscribe, context) + .then(() => { + should(kuzzle.hotelClerk.removeRooms(requestObject)).rejectedWith(BadRequestError); + }); + }); + it('should remove room in global subscription for provided collection', function () { var requestObjectSubscribe = new RequestObject({ controller: 'subscribe', action: 'on', requestId: roomName, + index: index, collection: collection1, body: {} }), @@ -94,6 +137,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'admin', action: 'removeRooms', requestId: roomName, + index: index, collection: collection1, body: {} }); @@ -117,6 +161,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'subscribe', action: 'on', requestId: roomName, + index: index, collection: collection1, body: filter1 }), @@ -124,6 +169,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'admin', action: 'removeRooms', requestId: roomName, + index: index, collection: collection1, body: {} }); @@ -147,6 +193,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'subscribe', action: 'on', requestId: roomName, + index: index, collection: collection1, body: {} }), @@ -154,6 +201,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'subscribe', action: 'on', requestId: roomName, + index: index, collection: collection1, body: filter1 }), @@ -161,6 +209,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'admin', action: 'removeRooms', requestId: roomName, + index: index, collection: collection1, body: {} }); @@ -187,6 +236,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'subscribe', action: 'on', requestId: roomName, + index: index, collection: collection1, body: {} }), @@ -194,6 +244,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'subscribe', action: 'on', requestId: roomName, + index: index, collection: collection2, body: {} }), @@ -201,6 +252,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'admin', action: 'removeRooms', requestId: roomName, + index: index, collection: collection1, body: {} }); @@ -221,8 +273,9 @@ describe('Test: hotelClerk.removeRooms', function () { should(kuzzle.dsl.filtersTree).be.Object(); should(Object.keys(kuzzle.dsl.filtersTree).length).be.exactly(1); - should(kuzzle.dsl.filtersTree[collection1]).be.undefined(); - should(kuzzle.dsl.filtersTree[collection2]).be.Object(); + should(Object.keys(kuzzle.dsl.filtersTree[index]).length).be.exactly(1); + should(kuzzle.dsl.filtersTree[index][collection1]).be.undefined(); + should(kuzzle.dsl.filtersTree[index][collection2]).be.Object(); }); }); @@ -232,6 +285,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'subscribe', action: 'on', requestId: roomName, + index: index, collection: collection1, body: {} }), @@ -239,6 +293,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'admin', action: 'removeRooms', requestId: roomName, + index: index, collection: collection1, body: {rooms: {}} }); @@ -251,11 +306,12 @@ describe('Test: hotelClerk.removeRooms', function () { it('should remove only listed rooms for the collection', function () { var - roomName = '3e0e837b447bf16b2251025ad36f39ed', + roomName = '9a83647ec2913bee3f3c1549c8a1ee7e', requestObjectSubscribeFilter1 = new RequestObject({ controller: 'subscribe', action: 'on', requestId: roomName, + index: index, collection: collection1, body: filter1 }), @@ -263,6 +319,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'subscribe', action: 'on', requestId: roomName, + index: index, collection: collection1, body: filter2 }), @@ -270,6 +327,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'admin', action: 'removeRooms', requestId: roomName, + index: index, collection: collection1, body: {rooms: [roomName]} }); @@ -289,11 +347,12 @@ describe('Test: hotelClerk.removeRooms', function () { it('should return a response with partial error if a roomId doesn\'t correspond to the collection', function () { var - badRoomName = 'c5fbe423c70b2924d9eebbc285a1b983', + badRoomName = '0ca6c2f9b4cc6450a63e3fe848ec7138', requestObjectSubscribe1 = new RequestObject({ controller: 'subscribe', action: 'on', requestId: roomName, + index: index, collection: collection1, body: {} }), @@ -301,6 +360,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'subscribe', action: 'on', requestId: roomName, + index: index, collection: collection2, body: {} }), @@ -308,6 +368,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'admin', action: 'removeRooms', requestId: roomName, + index: index, collection: collection1, body: {rooms: [badRoomName]} }); @@ -335,6 +396,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'subscribe', action: 'on', requestId: roomName, + index: index, collection: collection1, body: {} }), @@ -342,6 +404,7 @@ describe('Test: hotelClerk.removeRooms', function () { controller: 'admin', action: 'removeRooms', requestId: roomName, + index: index, collection: collection1, body: {rooms: [badRoomName]} }); diff --git a/test/api/core/hotelClerck/removeCustomerFromAllRooms.test.js b/test/api/core/hotelClerck/removeCustomerFromAllRooms.test.js index 7e3d56b258..3f2fcd36e2 100644 --- a/test/api/core/hotelClerck/removeCustomerFromAllRooms.test.js +++ b/test/api/core/hotelClerck/removeCustomerFromAllRooms.test.js @@ -19,21 +19,23 @@ describe('Test: hotelClerk.removeCustomerFromAllRooms', function () { roomName1 = 'roomName', roomName2 = 'roomName2', collection = 'user', + index = '%test', filter1 = { - term: { - firstName: 'Ada' - } - }, - filter2 = { - terms: { - firstName: ['Ada', 'Grace'] - } - }, + term: { + firstName: 'Ada' + } + }, + filter2 = { + terms: { + firstName: ['Ada', 'Grace'] + } + }, requestObject1 = new RequestObject({ controller: 'subscribe', action: 'on', requestId: roomName1, collection: collection, + index: index, body: filter1 }), notified, @@ -72,6 +74,7 @@ describe('Test: hotelClerk.removeCustomerFromAllRooms', function () { action: 'on', requestId: roomName2, collection: collection, + index: index, body: filter2 }); diff --git a/test/api/core/hotelClerck/removeSubscription.test.js b/test/api/core/hotelClerck/removeSubscription.test.js index 9f4bd3304e..5844057e9d 100644 --- a/test/api/core/hotelClerck/removeSubscription.test.js +++ b/test/api/core/hotelClerck/removeSubscription.test.js @@ -24,6 +24,7 @@ describe('Test: hotelClerk.removeSubscription', function () { badConnection = {id: 'badconnectionid'}, roomName1 = 'roomName1', roomName2 = 'roomName2', + index = 'test', collection = 'user', filter1 = { term: { @@ -39,6 +40,7 @@ describe('Test: hotelClerk.removeSubscription', function () { controller: 'subscribe', action: 'on', requestId: roomName1, + index: index, collection: collection, body: filter1 }), @@ -46,6 +48,7 @@ describe('Test: hotelClerk.removeSubscription', function () { controller: 'subscribe', action: 'on', requestId: roomName2, + index: index, collection: collection, body: filter2 }), @@ -84,6 +87,7 @@ describe('Test: hotelClerk.removeSubscription', function () { unsubscribeRequest = new RequestObject({ controller: 'subscribe', action: 'off', + index: index, collection: collection, body: { roomId: roomId } }); @@ -105,6 +109,7 @@ describe('Test: hotelClerk.removeSubscription', function () { var badRequestObject = new RequestObject({ controller: 'subscribe', action: 'on', + index: index, collection: collection, body: filter1 }); @@ -116,6 +121,7 @@ describe('Test: hotelClerk.removeSubscription', function () { var badRequestObject = new RequestObject({ controller: 'subscribe', action: 'on', + index: index, collection: collection, body: { roomId: 'this is not a room ID' } }); diff --git a/test/api/core/models/repositories/repository.test.js b/test/api/core/models/repositories/repository.test.js index 37e4741432..c5391a1ee3 100644 --- a/test/api/core/models/repositories/repository.test.js +++ b/test/api/core/models/repositories/repository.test.js @@ -34,16 +34,16 @@ describe('Test: repositories/repository', function () { mockCacheEngine = { get: function (key) { - if (key === repository.collection + '/persisted') { + if (key === repository.index + '/' + repository.collection + '/persisted') { return Promise.resolve(persistedObject); } - if (key === repository.collection + '/cached') { + if (key === repository.index + '/' + repository.collection + '/cached') { return Promise.resolve(cachedObject); } - if (key === repository.collection + '/error') { + if (key === repository.index + '/' + repository.collection + '/error') { return Promise.reject(new InternalError('Error')); } - if (key === repository.collection + '/string') { + if (key === repository.index + '/' + repository.collection + '/string') { return Promise.resolve('a string'); } @@ -90,6 +90,7 @@ describe('Test: repositories/repository', function () { action: 'get', requestId: 'foo', collection: repository.collection, + index: repository.index, body: { _id: id } @@ -123,7 +124,8 @@ describe('Test: repositories/repository', function () { }; repository = new Repository(mockKuzzle, { - collection: '_test/repository', + index: '%test', + collection: 'repository', ObjectConstructor: ObjectConstructor, readEngine: mockReadEngine, writeEngine: mockWriteEngine, diff --git a/test/api/core/models/repositories/userrepository.test.js b/test/api/core/models/repositories/userRepository.test.js similarity index 98% rename from test/api/core/models/repositories/userrepository.test.js rename to test/api/core/models/repositories/userRepository.test.js index 2fa4b7e63c..6046cacd1a 100644 --- a/test/api/core/models/repositories/userrepository.test.js +++ b/test/api/core/models/repositories/userRepository.test.js @@ -32,7 +32,7 @@ before(function (done) { mockCacheEngine = { get: function (key) { - if (key === userRepository.collection + '/userInCache') { + if (key === userRepository.index + '/' + userRepository.collection + '/userInCache') { return Promise.resolve(userInCache); } return Promise.resolve(null); @@ -179,7 +179,7 @@ describe('Test: repositories/userRepository', function () { var token; - token = jwt.sign({_id: -99999}, params.jsonWebToken.secret, {algorithm: params.jsonWebToken.algorithm}); + token = jwt.sign({_id: -99999}, params.jsonWebToken.secret, {algorithm: params.jsonWebToken.algorithm}); userRepository.loadFromToken(token) .then(function (user) { diff --git a/test/api/core/models/responseObject.test.js b/test/api/core/models/responseObject.test.js index ff7b3b084c..7737181ed9 100644 --- a/test/api/core/models/responseObject.test.js +++ b/test/api/core/models/responseObject.test.js @@ -20,6 +20,7 @@ describe('Test: responseObject', function () { request = { action: 'fakeaction', controller: 'fakecontroller', + index: '%test', collection: 'fakecollection', persist: 'maybe', requestId: 'fakerequestId', diff --git a/test/api/core/notifier/checkNewRoutes.test.js b/test/api/core/notifier/checkNewRoutes.test.js index e4f07e3157..6d6ab7c919 100644 --- a/test/api/core/notifier/checkNewRoutes.test.js +++ b/test/api/core/notifier/checkNewRoutes.test.js @@ -30,15 +30,19 @@ describe('Test: notifier.checkNewRoutes', function () { 'putMapping', 'getMapping', 'listCollections', - 'reset', + 'deleteIndexes', 'createCollection', - 'truncateCollection' + 'truncateCollection', + 'createIndex', + 'deleteIndex', + 'listIndexes' ], requestObject = new RequestObject({ controller: 'write', action: '', requestId: 'foo', collection: 'bar', + index: '%test', body: { foo: 'bar' } }), responseObject = new ResponseObject(requestObject, {}); diff --git a/test/api/dsl/index/addCollectionSubscription.test.js b/test/api/dsl/index/addCollectionSubscription.test.js index 8d22f9e5b1..9f6f22ed47 100644 --- a/test/api/dsl/index/addCollectionSubscription.test.js +++ b/test/api/dsl/index/addCollectionSubscription.test.js @@ -5,36 +5,39 @@ var require('should-promised'); describe('Test: dsl.addCollectionSubscription', function () { - var dsl; + var dsl, + index = 'test'; beforeEach(function () { dsl = new Dsl(); }); it('should return a resolved promise when adding a new collection', function () { - return should(dsl.addCollectionSubscription('foo', 'bar')).be.fulfilled(); + return should(dsl.addCollectionSubscription('foo', index, 'bar')).be.fulfilled(); }); it('should initialize the filtersTree object correctly when adding a new collection', function () { - return dsl.addCollectionSubscription('foo', 'bar') + return dsl.addCollectionSubscription('foo', index, 'bar') .then(function () { - should(dsl.filtersTree.bar).be.an.Object(); - should(dsl.filtersTree.bar.rooms).be.an.Array(); - should(dsl.filtersTree.bar.rooms.length).be.exactly(1); - should(dsl.filtersTree.bar.rooms[0]).be.exactly('foo'); + should(dsl.filtersTree[index]).be.an.Object(); + should(dsl.filtersTree[index].bar).be.an.Object(); + should(dsl.filtersTree[index].bar.rooms).be.an.Array(); + should(dsl.filtersTree[index].bar.rooms.length).be.exactly(1); + should(dsl.filtersTree[index].bar.rooms[0]).be.exactly('foo'); }); }); it('should not add the same room multiple times in the same collection', function () { - return dsl.addCollectionSubscription('foo', 'bar') + return dsl.addCollectionSubscription('foo', index, 'bar') .then(function () { - return dsl.addCollectionSubscription('foo', 'bar'); + return dsl.addCollectionSubscription('foo', index, 'bar'); }) .then(function () { - should(dsl.filtersTree.bar).be.an.Object(); - should(dsl.filtersTree.bar.rooms).be.an.Array(); - should(dsl.filtersTree.bar.rooms.length).be.exactly(1); - should(dsl.filtersTree.bar.rooms[0]).be.exactly('foo'); + should(dsl.filtersTree[index]).be.an.Object(); + should(dsl.filtersTree[index].bar).be.an.Object(); + should(dsl.filtersTree[index].bar.rooms).be.an.Array(); + should(dsl.filtersTree[index].bar.rooms.length).be.exactly(1); + should(dsl.filtersTree[index].bar.rooms[0]).be.exactly('foo'); }); }); }); diff --git a/test/api/dsl/index/addCurriedFunction.test.js b/test/api/dsl/index/addCurriedFunction.test.js index 808406e4f9..44f829b476 100644 --- a/test/api/dsl/index/addCurriedFunction.test.js +++ b/test/api/dsl/index/addCurriedFunction.test.js @@ -9,6 +9,7 @@ describe('Test: dsl.addCurriedFunction', function () { var dsl, roomId = 'roomId', + index = 'index', collection = 'user', fakeFilter = { fakeFilter: { @@ -27,19 +28,19 @@ describe('Test: dsl.addCurriedFunction', function () { it('should return an error when the filter is undefined', function () { - return should(dsl.addCurriedFunction(roomId, collection, undefined)).be.rejected(); + return should(dsl.addCurriedFunction(roomId, index, collection, undefined)).be.rejected(); }); it('should return an error when the filter doesn\'t exist', function () { - return should(dsl.addCurriedFunction(roomId, collection, fakeFilter)).be.rejected(); + return should(dsl.addCurriedFunction(roomId, index, collection, fakeFilter)).be.rejected(); }); it('should return an error when the filter is empty', function () { - return should(dsl.addCurriedFunction(roomId, collection, {})).be.rejected(); + return should(dsl.addCurriedFunction(roomId, index, collection, {})).be.rejected(); }); it('should resolve a promise when the filter exists', function () { - return should(dsl.addCurriedFunction(roomId, collection, filter)).not.be.rejected(); + return should(dsl.addCurriedFunction(roomId, index, collection, filter)).not.be.rejected(); }); }); diff --git a/test/api/dsl/index/removeFilterPath.test.js b/test/api/dsl/index/removeFilterPath.test.js index a3e4f876a8..ce1956bbe7 100644 --- a/test/api/dsl/index/removeFilterPath.test.js +++ b/test/api/dsl/index/removeFilterPath.test.js @@ -11,13 +11,15 @@ describe('Test: dsl.removeFilterPath', function () { beforeEach(function () { dsl = new Dsl(); dsl.filtersTree = { - aCollection: { - rooms: [], - fields: { - aField: { - randomFilter: { - rooms: [], - fn: function () { } + anIndex: { + aCollection: { + rooms: [], + fields: { + aField: { + randomFilter: { + rooms: [], + fn: function () { } + } } } } @@ -26,53 +28,53 @@ describe('Test: dsl.removeFilterPath', function () { }); it('should not delete the entire filter if another room use it', function () { - dsl.filtersTree.aCollection.fields.aField.randomFilter.rooms = [ 'foo', 'bar' ]; - removeFilterPath.call(dsl, {id: 'bar'}, 'aCollection.aField.randomFilter'); + dsl.filtersTree.anIndex.aCollection.fields.aField.randomFilter.rooms = [ 'foo', 'bar' ]; + removeFilterPath.call(dsl, {id: 'bar'}, 'anIndex.aCollection.aField.randomFilter'); - should.exist(dsl.filtersTree.aCollection); - should.exist(dsl.filtersTree.aCollection.fields); - should.exist(dsl.filtersTree.aCollection.fields.aField); - should.exist(dsl.filtersTree.aCollection.fields.aField.randomFilter); - should(dsl.filtersTree.aCollection.fields.aField.randomFilter.rooms).be.an.Array().and.match(['foo']); + should.exist(dsl.filtersTree.anIndex.aCollection); + should.exist(dsl.filtersTree.anIndex.aCollection.fields); + should.exist(dsl.filtersTree.anIndex.aCollection.fields.aField); + should.exist(dsl.filtersTree.anIndex.aCollection.fields.aField.randomFilter); + should(dsl.filtersTree.anIndex.aCollection.fields.aField.randomFilter.rooms).be.an.Array().and.match(['foo']); }); it('should do nothing if the provided room is not listed in the filter path', function () { var rooms = [ 'foo', 'bar' ]; - dsl.filtersTree.aCollection.fields.aField.randomFilter.rooms = rooms; - removeFilterPath.call(dsl, {id: 'foobar'}, 'aCollection.aField.randomFilter'); + dsl.filtersTree.anIndex.aCollection.fields.aField.randomFilter.rooms = rooms; + removeFilterPath.call(dsl, {id: 'foobar'}, 'anIndex.aCollection.aField.randomFilter'); - should.exist(dsl.filtersTree.aCollection); - should.exist(dsl.filtersTree.aCollection.fields); - should.exist(dsl.filtersTree.aCollection.fields.aField); - should.exist(dsl.filtersTree.aCollection.fields.aField.randomFilter); - should(dsl.filtersTree.aCollection.fields.aField.randomFilter.rooms).be.an.Array().and.match(rooms); + should.exist(dsl.filtersTree.anIndex.aCollection); + should.exist(dsl.filtersTree.anIndex.aCollection.fields); + should.exist(dsl.filtersTree.anIndex.aCollection.fields.aField); + should.exist(dsl.filtersTree.anIndex.aCollection.fields.aField.randomFilter); + should(dsl.filtersTree.anIndex.aCollection.fields.aField.randomFilter.rooms).be.an.Array().and.match(rooms); }); it('should remove the entire filter path if there is no room left', function () { - dsl.filtersTree.aCollection.fields.aField.randomFilter.rooms = [ 'foo' ]; - removeFilterPath.call(dsl, {id: 'foo'}, 'aCollection.aField.randomFilter'); + dsl.filtersTree.anIndex.aCollection.fields.aField.randomFilter.rooms = [ 'foo' ]; + removeFilterPath.call(dsl, {id: 'foo'}, 'anIndex.aCollection.aField.randomFilter'); should(dsl.filtersTree).be.an.Object().and.be.empty(); }); it('should not delete any other filter than the one we provided', function () { - dsl.filtersTree.aCollection.fields.aField.randomFilter.rooms = [ 'foo' ]; - dsl.filtersTree.aCollection.fields.anotherField = { + dsl.filtersTree.anIndex.aCollection.fields.aField.randomFilter.rooms = [ 'foo' ]; + dsl.filtersTree.anIndex.aCollection.fields.anotherField = { anotherFilter: { rooms: [ 'foo' ], fn: function () {} } }; - removeFilterPath.call(dsl, {id: 'foo'}, 'aCollection.aField.randomFilter'); + removeFilterPath.call(dsl, {id: 'foo'}, 'anIndex.aCollection.aField.randomFilter'); - should.exist(dsl.filtersTree.aCollection); - should.exist(dsl.filtersTree.aCollection.fields); - should.not.exist(dsl.filtersTree.aCollection.fields.aField); - should.exist(dsl.filtersTree.aCollection.fields.anotherField); - should.exist(dsl.filtersTree.aCollection.fields.anotherField.anotherFilter); - should.exist(dsl.filtersTree.aCollection.fields.anotherField.anotherFilter.rooms); - should(dsl.filtersTree.aCollection.fields.anotherField.anotherFilter.rooms).match(['foo']); + should.exist(dsl.filtersTree.anIndex.aCollection); + should.exist(dsl.filtersTree.anIndex.aCollection.fields); + should.not.exist(dsl.filtersTree.anIndex.aCollection.fields.aField); + should.exist(dsl.filtersTree.anIndex.aCollection.fields.anotherField); + should.exist(dsl.filtersTree.anIndex.aCollection.fields.anotherField.anotherFilter); + should.exist(dsl.filtersTree.anIndex.aCollection.fields.anotherField.anotherFilter.rooms); + should(dsl.filtersTree.anIndex.aCollection.fields.anotherField.anotherFilter.rooms).match(['foo']); }); }); diff --git a/test/api/dsl/index/removeRoom.test.js b/test/api/dsl/index/removeRoom.test.js index f969616fbf..7d2467eef9 100644 --- a/test/api/dsl/index/removeRoom.test.js +++ b/test/api/dsl/index/removeRoom.test.js @@ -17,6 +17,7 @@ describe('Test removeRoom function index.js file from DSL', function () { roomId, anonymousUser, roomName = 'roomNameGrace', + index = 'test', collection = 'user', filter = { terms: { @@ -25,6 +26,7 @@ describe('Test removeRoom function index.js file from DSL', function () { }, requestObject = new RequestObject({ requestId: roomName, + index: index, collection: collection, body: filter }); diff --git a/test/api/dsl/index/removeRoomFromGlobal.test.js b/test/api/dsl/index/removeRoomFromGlobal.test.js index 0b7f387f88..f6f82b4820 100644 --- a/test/api/dsl/index/removeRoomFromGlobal.test.js +++ b/test/api/dsl/index/removeRoomFromGlobal.test.js @@ -13,7 +13,7 @@ describe('Test: dsl.removeRoomFromGlobal', function () { }); it('should do nothing if the collection does not exist', function () { - var room = { collection: 'foobar' }; + var room = { index: 'test', collection: 'foobar' }; should(removeRoomFromGlobal.call(dsl, room)).be.false(); @@ -25,21 +25,21 @@ describe('Test: dsl.removeRoomFromGlobal', function () { }); it('should do nothing if the room does not exist', function () { - var room = { collection: 'foo', id: 'bar' }; + var room = { index: 'test', collection: 'foo', id: 'bar' }; - dsl.addCollectionSubscription('foobar', room.collection); + dsl.addCollectionSubscription('foobar', room.index, room.collection); removeRoomFromGlobal.call(dsl, room); - should(dsl.filtersTree.foo.rooms.length).be.exactly(1); - should(dsl.filtersTree.foo.rooms[0]).be.exactly('foobar'); + should(dsl.filtersTree.test.foo.rooms.length).be.exactly(1); + should(dsl.filtersTree.test.foo.rooms[0]).be.exactly('foobar'); }); it('should remove an existing room from the collection', function () { - var room = { collection: 'foo', id: 'bar' }; + var room = { index: 'test', collection: 'foo', id: 'bar' }; - dsl.addCollectionSubscription(room.id, room.collection); + dsl.addCollectionSubscription(room.id, room.index, room.collection); removeRoomFromGlobal.call(dsl, room); - should(dsl.filtersTree.foo).be.undefined(); + should(dsl.filtersTree.test).be.undefined(); }); }); diff --git a/test/api/dsl/index/testFieldFilters.test.js b/test/api/dsl/index/testFieldFilters.test.js index 68172789b5..8934ffce3a 100644 --- a/test/api/dsl/index/testFieldFilters.test.js +++ b/test/api/dsl/index/testFieldFilters.test.js @@ -12,6 +12,7 @@ describe('Test: dsl.testFieldFilters', function () { dsl, requestObject = new RequestObject({ requestId: 'foo', + index: 'test', collection: 'bar', body: { foo: 'bar' } }); @@ -36,10 +37,11 @@ describe('Test: dsl.testFieldFilters', function () { return should(result).be.fulfilledWith([]); }); - it('should resolve to an empty arry if no field is registered on a given collection', function () { + it('should resolve to an empty array if no field is registered on a given collection', function () { var result; - dsl.filtersTree[requestObject.collection] = {}; + dsl.filtersTree[requestObject.index] = {}; + dsl.filtersTree[requestObject.index][requestObject.collection] = {}; result = testFieldFilters.call(dsl, {}, requestObject, {}); return should(result).be.fulfilledWith([]); @@ -48,7 +50,8 @@ describe('Test: dsl.testFieldFilters', function () { it('should resolve to an empty array if the tested fields aren\'t listed on a given collection', function () { var result; - dsl.filtersTree[requestObject.collection] = { fields: { foobar: '' }}; + dsl.filtersTree[requestObject.index] = {}; + dsl.filtersTree[requestObject.index][requestObject.collection] = { fields: { foobar: '' }}; result = testFieldFilters.call(dsl, requestObject, {}, {}); return should(result).be.fulfilledWith([]); @@ -58,7 +61,8 @@ describe('Test: dsl.testFieldFilters', function () { var result; - dsl.filtersTree[requestObject.collection] = { + dsl.filtersTree[requestObject.index] = {}; + dsl.filtersTree[requestObject.index][requestObject.collection] = { fields: { foobar: { testFoobar: { @@ -80,7 +84,8 @@ describe('Test: dsl.testFieldFilters', function () { result, rooms = [ 'foo', 'bar', 'baz' ]; - dsl.filtersTree[requestObject.collection] = { + dsl.filtersTree[requestObject.index] = {}; + dsl.filtersTree[requestObject.index][requestObject.collection] = { fields: { 'foo.bar': { testFoobar: { @@ -102,7 +107,8 @@ describe('Test: dsl.testFieldFilters', function () { result, rooms = [ 'foo', 'bar', 'baz' ]; - dsl.filtersTree[requestObject.collection] = { + dsl.filtersTree[requestObject.index] = {}; + dsl.filtersTree[requestObject.index][requestObject.collection] = { fields: { 'foo.bar': { testFoobar: { diff --git a/test/api/dsl/index/testFilters.test.js b/test/api/dsl/index/testFilters.test.js index af9242a616..d18f5f3766 100644 --- a/test/api/dsl/index/testFilters.test.js +++ b/test/api/dsl/index/testFilters.test.js @@ -17,6 +17,7 @@ describe('Test: dsl.testFilters', function () { anonymousUser, roomId, roomName = 'roomNameGrace', + index = 'index', collection = 'user', dataGrace = { firstName: 'Grace', @@ -84,16 +85,19 @@ describe('Test: dsl.testFilters', function () { requestObjectCreateGrace = new RequestObject({ requestId: roomName, + index: index, collection: collection, body: dataGrace }), requestObjectSubscribeGrace = new RequestObject({ requestId: roomName, + index: index, collection: collection, body: filterGrace }), requestObjectCreateAda = new RequestObject({ requestId: roomName, + index: index, collection: collection, body: dataAda }); @@ -147,9 +151,20 @@ describe('Test: dsl.testFilters', function () { }); }); + it('should return an error if the requestObject doesn\'t contain a index name', function () { + var requestObject = new RequestObject({ + requestId: roomName, + collection: 'test', + body: dataGrace + }); + + return should(kuzzle.dsl.testFilters(requestObject)).be.rejected(); + }); + it('should return an error if the requestObject doesn\'t contain a collection name', function () { var requestObject = new RequestObject({ requestId: roomName, + index: 'test', body: dataGrace }); @@ -173,7 +188,8 @@ describe('Test: dsl.testFilters', function () { testFieldFilters: function () { return Promise.reject(new Error('rejected')); } })(function () { var dsl = new Dsl(kuzzle); - dsl.filtersTree[requestObjectCreateGrace.collection] = {}; + dsl.filtersTree[requestObjectCreateGrace.index] = {}; + dsl.filtersTree[requestObjectCreateGrace.index][requestObjectCreateGrace.collection] = {}; dsl.testFilters(requestObjectCreateGrace); }); }); @@ -195,7 +211,8 @@ describe('Test: dsl.testFilters', function () { testGlobalsFilters: function () { return Promise.reject(new Error('rejected')); } })(function () { var dsl = new Dsl(kuzzle); - dsl.filtersTree[requestObjectCreateGrace.collection] = {}; + dsl.filtersTree[requestObjectCreateGrace.index] = {}; + dsl.filtersTree[requestObjectCreateGrace.index][requestObjectCreateGrace.collection] = {}; dsl.testFilters(requestObjectCreateGrace); }); }); diff --git a/test/api/dsl/index/testGlobalsFilter.test.js b/test/api/dsl/index/testGlobalsFilter.test.js index 069c4d0f58..7153662f77 100644 --- a/test/api/dsl/index/testGlobalsFilter.test.js +++ b/test/api/dsl/index/testGlobalsFilter.test.js @@ -14,6 +14,7 @@ describe('Test: dsl.testGlobalsFilters', function () { cachedResult = { Goldorak: 'GO!' }, requestObject = new RequestObject({ requestId: 'foo', + index: 'test', collection: 'bar', body: {foo: 'bar'} }); @@ -27,7 +28,8 @@ describe('Test: dsl.testGlobalsFilters', function () { }); dsl = new Dsl(); - dsl.filtersTree[requestObject.collection] = {}; + dsl.filtersTree[requestObject.index] = {}; + dsl.filtersTree[requestObject.index][requestObject.collection] = {}; }); it('should resolve to an empty array if there if the collection doesn\'t contain a room array', function () { @@ -35,14 +37,14 @@ describe('Test: dsl.testGlobalsFilters', function () { }); it('should resolve to an empty array if there is no rooms registered on that collection', function () { - dsl.filtersTree[requestObject.collection].rooms = []; + dsl.filtersTree[requestObject.index][requestObject.collection].rooms = []; return should(testGlobalsFilters.call(dsl, requestObject, flattenBody, cachedResult)).be.fulfilledWith([]); }); it('should call the testRooms with the appropriate set of arguments', function () { var rooms = ['Excuse', 'me', 'while', 'I', 'kiss', 'the', 'sky']; - dsl.filtersTree[requestObject.collection].rooms = rooms; + dsl.filtersTree[requestObject.index][requestObject.collection].rooms = rooms; return should(testGlobalsFilters.call(dsl, requestObject, flattenBody, cachedResult)).be.fulfilledWith(rooms); }); }); \ No newline at end of file diff --git a/test/api/dsl/methods/and.test.js b/test/api/dsl/methods/and.test.js index 65fbb9caee..8b28a3c5fb 100644 --- a/test/api/dsl/methods/and.test.js +++ b/test/api/dsl/methods/and.test.js @@ -9,6 +9,7 @@ describe('Test and method', function () { var roomId = 'roomId', + index = 'index', collection = 'collection', documentGrace = { firstName: 'Grace', @@ -38,31 +39,32 @@ describe('Test and method', function () { before(function () { methods.dsl.filtersTree = {}; - return methods.and(roomId, collection, filter); + return methods.and(roomId, index, collection, filter); }); it('should construct the filterTree object for the correct attribute', function () { should(methods.dsl.filtersTree).not.be.empty(); - should(methods.dsl.filtersTree[collection]).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.city).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.hobby).not.be.empty(); + should(methods.dsl.filtersTree[index]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.city).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.hobby).not.be.empty(); }); it('should construct the filterTree with correct curried function name', function () { - should(methods.dsl.filtersTree[collection].fields.city.termcityNYC).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.hobby.termhobbycomputer).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.city.termcityNYC).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.hobby.termhobbycomputer).not.be.empty(); }); it('should construct the filterTree with correct room list', function () { var rooms; - rooms = methods.dsl.filtersTree[collection].fields.city.termcityNYC.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.city.termcityNYC.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.hobby.termhobbycomputer.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.hobby.termhobbycomputer.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); @@ -71,14 +73,14 @@ describe('Test and method', function () { it('should construct the filterTree with correct functions', function () { var result; - result = methods.dsl.filtersTree[collection].fields.city.termcityNYC.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.city.termcityNYC.fn(documentGrace); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.city.termcityNYC.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.city.termcityNYC.fn(documentAda); should(result).be.exactly(false); - result = methods.dsl.filtersTree[collection].fields.hobby.termhobbycomputer.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.hobby.termhobbycomputer.fn(documentGrace); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.hobby.termhobbycomputer.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.hobby.termhobbycomputer.fn(documentAda); should(result).be.exactly(true); }); @@ -86,7 +88,7 @@ describe('Test and method', function () { return methods.__with__({ getFormattedFilters: function () { return Promise.reject(new Error('rejected')); } })(function () { - return should(methods.and(roomId, collection, filter)).be.rejectedWith('rejected'); + return should(methods.and(roomId, index, collection, filter)).be.rejectedWith('rejected'); }); }); }); \ No newline at end of file diff --git a/test/api/dsl/methods/bool.test.js b/test/api/dsl/methods/bool.test.js index 382b4611ae..3e502859e1 100644 --- a/test/api/dsl/methods/bool.test.js +++ b/test/api/dsl/methods/bool.test.js @@ -10,6 +10,7 @@ describe('Test bool method', function () { var roomId = 'roomId', + index = 'test', collection = 'collection', documentGrace = { firstName: 'Grace', @@ -65,60 +66,58 @@ describe('Test bool method', function () { before(function () { methods.dsl.filtersTree = {}; - return methods.bool(roomId, collection, filter); + return methods.bool(roomId, index, collection, filter); }); it('should construct the filterTree object for the correct attribute', function () { should(methods.dsl.filtersTree).not.be.empty(); - should(methods.dsl.filtersTree[collection]).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields).not.be.empty(); - - should(methods.dsl.filtersTree[collection].fields.firstName).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.age).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.city).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.hobby).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.lastName).not.be.empty(); + should(methods.dsl.filtersTree[index]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.firstName).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.age).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.city).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.hobby).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.lastName).not.be.empty(); }); it('should construct the filterTree with correct curried function name', function () { - should(methods.dsl.filtersTree[collection].fields.firstName['termsfirstNameGrace,Ada']).not.be.empty(); - - should(methods.dsl.filtersTree[collection].fields.age.rangeagegte36).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.age.rangeagelt85).not.be.empty(); - - should(methods.dsl.filtersTree[collection].fields.city.nottermcityNYC).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.hobby.termhobbycomputer).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.lastName.existslastName).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.firstName['termsfirstNameGrace,Ada']).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.age.rangeagegte36).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.age.rangeagelt85).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.city.nottermcityNYC).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.hobby.termhobbycomputer).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.lastName.existslastName).not.be.empty(); }); it('should construct the filterTree with correct room list', function () { var rooms; - rooms = methods.dsl.filtersTree[collection].fields.firstName['termsfirstNameGrace,Ada'].rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.firstName['termsfirstNameGrace,Ada'].rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.age.rangeagegte36.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.age.rangeagegte36.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.age.rangeagelt85.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.age.rangeagelt85.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.city.nottermcityNYC.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.city.nottermcityNYC.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.hobby.termhobbycomputer.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.hobby.termhobbycomputer.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.lastName.existslastName.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.lastName.existslastName.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); @@ -127,51 +126,51 @@ describe('Test bool method', function () { it('should construct the filterTree with correct functions', function () { var result; - result = methods.dsl.filtersTree[collection].fields.firstName['termsfirstNameGrace,Ada'].fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.firstName['termsfirstNameGrace,Ada'].fn(documentGrace); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.firstName['termsfirstNameGrace,Ada'].fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.firstName['termsfirstNameGrace,Ada'].fn(documentAda); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.age.rangeagegte36.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.age.rangeagegte36.fn(documentGrace); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.age.rangeagegte36.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.age.rangeagegte36.fn(documentAda); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.age.rangeagelt85.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.age.rangeagelt85.fn(documentGrace); should(result).be.exactly(false); - result = methods.dsl.filtersTree[collection].fields.age.rangeagelt85.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.age.rangeagelt85.fn(documentAda); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.city.nottermcityNYC.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.city.nottermcityNYC.fn(documentGrace); should(result).be.exactly(false); - result = methods.dsl.filtersTree[collection].fields.city.nottermcityNYC.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.city.nottermcityNYC.fn(documentAda); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.hobby.termhobbycomputer.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.hobby.termhobbycomputer.fn(documentGrace); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.hobby.termhobbycomputer.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.hobby.termhobbycomputer.fn(documentAda); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.lastName.existslastName.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.lastName.existslastName.fn(documentGrace); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.lastName.existslastName.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.lastName.existslastName.fn(documentAda); should(result).be.exactly(true); }); it('should return a rejected promise if an empty filter is provided', function () { - return should(methods.bool(roomId, collection, {})).be.rejectedWith(BadRequestError, { message: 'A filter can\'t be empty' }); + return should(methods.bool(roomId, index, collection, {})).be.rejectedWith(BadRequestError, { message: 'A filter can\'t be empty' }); }); it('should return a rejected promise if the filter contains an invalid key', function () { var f = { foo: 'bar' }; - return should(methods.bool(roomId, collection, f)).be.rejectedWith(BadRequestError, { message: 'Function foo doesn\'t exist' }); + return should(methods.bool(roomId, index, collection, f)).be.rejectedWith(BadRequestError, { message: 'Function foo doesn\'t exist' }); }); it('should return a rejected promise if one of the bool sub-methods fails', function () { var f = { must: [ { foo: 'bar' } ] }; methods.must = function () { return Promise.reject(new Error('rejected')); }; - return should(methods.bool(roomId, collection, f)).be.rejectedWith('rejected'); + return should(methods.bool(roomId, index, collection, f)).be.rejectedWith('rejected'); }); }); \ No newline at end of file diff --git a/test/api/dsl/methods/buildCurriedFunction.test.js b/test/api/dsl/methods/buildCurriedFunction.test.js index 23a7f0a86a..61ab4000a3 100644 --- a/test/api/dsl/methods/buildCurriedFunction.test.js +++ b/test/api/dsl/methods/buildCurriedFunction.test.js @@ -12,7 +12,7 @@ describe('Test: dsl.buildCurriedFunction method', function () { }); it('should return an error if the provided operator is not supported', function () { - var result = buildCurriedFunction('', '', 'foobar', '', '', '', false, false); + var result = buildCurriedFunction('', '', '', 'foobar', '', '', '', false, false); should(result).be.an.Object(); should(result.status).be.exactly(400); should(result.message).not.be.empty(); @@ -20,40 +20,40 @@ describe('Test: dsl.buildCurriedFunction method', function () { }); it('should initializes the filter tree using the provided arguments', function () { - var result = buildCurriedFunction.call(methods, 'collection', 'field', 'gte', 42, 'filter', 'roomId'); + var result = buildCurriedFunction.call(methods, 'index', 'collection', 'field', 'gte', 42, 'filter', 'roomId'); should.not.exist(result.error); - should(result.path).be.exactly('collection.field.filter'); + should(result.path).be.exactly('index.collection.field.filter'); should.exist(result.filter); should(result.filter.rooms).be.an.Array().and.match(['roomId']); should(result.filter.rooms.length).be.eql(1); should(result.filter.fn).be.a.Function(); - should.exist(methods.dsl.filtersTree.collection); - should.exist(methods.dsl.filtersTree.collection.fields); - should.exist(methods.dsl.filtersTree.collection.fields.field); - should.exist(methods.dsl.filtersTree.collection.fields.field.filter); - should(methods.dsl.filtersTree.collection.fields.field.filter.rooms).be.an.Array().and.match(['roomId']); + should.exist(methods.dsl.filtersTree.index.collection); + should.exist(methods.dsl.filtersTree.index.collection.fields); + should.exist(methods.dsl.filtersTree.index.collection.fields.field); + should.exist(methods.dsl.filtersTree.index.collection.fields.field.filter); + should(methods.dsl.filtersTree.index.collection.fields.field.filter.rooms).be.an.Array().and.match(['roomId']); should(result.filter.rooms.length).be.eql(1); - should(methods.dsl.filtersTree.collection.fields.field.filter.fn).be.a.Function(); + should(methods.dsl.filtersTree.index.collection.fields.field.filter.fn).be.a.Function(); - should.not.exist(methods.dsl.filtersTree.collection.rooms); + should.not.exist(methods.dsl.filtersTree.index.collection.rooms); }); it('should not add a room to the same filter if it is already assigned to it', function () { - buildCurriedFunction.call(methods, 'collection', 'field', 'gte', 42, 'filter', 'roomId'); - buildCurriedFunction.call(methods, 'collection', 'field', 'gte', 42, 'filter', 'roomId'); + buildCurriedFunction.call(methods, 'index', 'collection', 'field', 'gte', 42, 'filter', 'roomId'); + buildCurriedFunction.call(methods, 'index', 'collection', 'field', 'gte', 42, 'filter', 'roomId'); - should(methods.dsl.filtersTree.collection.fields.field.filter.rooms).be.an.Array().and.match(['roomId']); - should(methods.dsl.filtersTree.collection.fields.field.filter.rooms.length).be.eql(1); + should(methods.dsl.filtersTree.index.collection.fields.field.filter.rooms).be.an.Array().and.match(['roomId']); + should(methods.dsl.filtersTree.index.collection.fields.field.filter.rooms.length).be.eql(1); }); it('should also add the room to the global rooms list if the filter is global', function () { - buildCurriedFunction.call(methods, 'collection', 'field', 'gte', 42, 'filter', 'roomId', false, true); - buildCurriedFunction.call(methods, 'collection', 'field', 'gte', 42, 'filter', 'roomId', false, true); + buildCurriedFunction.call(methods, 'index', 'collection', 'field', 'gte', 42, 'filter', 'roomId', false, true); + buildCurriedFunction.call(methods, 'index', 'collection', 'field', 'gte', 42, 'filter', 'roomId', false, true); - should.exist(methods.dsl.filtersTree.collection.rooms); - should(methods.dsl.filtersTree.collection.rooms).be.an.Array().and.match(['roomId']); - should(methods.dsl.filtersTree.collection.rooms.length).be.eql(1); + should.exist(methods.dsl.filtersTree.index.collection.rooms); + should(methods.dsl.filtersTree.index.collection.rooms).be.an.Array().and.match(['roomId']); + should(methods.dsl.filtersTree.index.collection.rooms.length).be.eql(1); }); }); \ No newline at end of file diff --git a/test/api/dsl/methods/exists.test.js b/test/api/dsl/methods/exists.test.js index 16f4f11f1b..5735f1b67f 100644 --- a/test/api/dsl/methods/exists.test.js +++ b/test/api/dsl/methods/exists.test.js @@ -11,6 +11,7 @@ describe('Test exists method', function () { var roomId = 'roomId', + index = 'test', collection = 'collection', documentGrace = { firstName: 'Grace', @@ -26,25 +27,26 @@ describe('Test exists method', function () { before(function () { methods.dsl.filtersTree = {}; - return methods.exists(roomId, collection, filter); + return methods.exists(roomId, index, collection, filter); }); it('should construct the filterTree object for the correct attribute', function () { should(methods.dsl.filtersTree).not.be.empty(); - should(methods.dsl.filtersTree[collection]).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.lastName).not.be.empty(); + should(methods.dsl.filtersTree[index]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.lastName).not.be.empty(); }); it('should construct the filterTree with correct curried function name', function () { - should(methods.dsl.filtersTree[collection].fields.lastName.existslastName).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.lastName.existslastName).not.be.empty(); }); it('should construct the filterTree with correct room list', function () { var rooms; // Test gt from filterGrace - rooms = methods.dsl.filtersTree[collection].fields.lastName.existslastName.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.lastName.existslastName.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); @@ -53,53 +55,53 @@ describe('Test exists method', function () { it('should construct the filterTree with correct functions exists', function () { var result; - result = methods.dsl.filtersTree[collection].fields.lastName.existslastName.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.lastName.existslastName.fn(documentGrace); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.lastName.existslastName.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.lastName.existslastName.fn(documentAda); should(result).be.exactly(false); }); it('should return a rejected promise if the filter argument is empty', function () { - return should(methods.exists('foo', 'bar', {})).be.rejectedWith(BadRequestError, { message: 'A filter can\'t be empty' }); + return should(methods.exists('foo', 'index', 'bar', {})).be.rejectedWith(BadRequestError, { message: 'A filter can\'t be empty' }); }); it('should return a rejected promise if the filter argument does not contain a "field" term', function () { - return should(methods.exists('foo', 'bar', { foo: 'bar' })).be.rejectedWith(BadRequestError, { message: 'Filter \'exists\' must contains \'field\' attribute' }); + return should(methods.exists('foo', 'index', 'bar', { foo: 'bar' })).be.rejectedWith(BadRequestError, { message: 'Filter \'exists\' must contains \'field\' attribute' }); }); it('should return a rejected promise if the "field" term does not contain a string', function () { - return should(methods.exists('foo', 'bar', { field: {foo: 'bar'} })).be.rejectedWith(BadRequestError); + return should(methods.exists('foo', 'index', 'bar', { field: {foo: 'bar'} })).be.rejectedWith(BadRequestError); }); it('should return a rejected promise if buildCurriedFunction fails', function () { return methods.__with__({ buildCurriedFunction: function () { return new InternalError('rejected'); } })(function () { - return should(methods.exists('foo', 'bar', { field: 'foo' })); + return should(methods.exists('foo', 'index', 'bar', { field: 'foo' })); }); }); it('should register the filter in the lcao area in case of a "exist" filter', function () { return methods.__with__({ - buildCurriedFunction: function (collection, field, operatorName, value, curriedFunctionName, roomId, not, inGlobals) { + buildCurriedFunction: function (index, collection, field, operatorName, value, curriedFunctionName, roomId, not, inGlobals) { should(inGlobals).be.false(); should(curriedFunctionName).not.startWith('not'); return { path: '' }; } })(function () { - return should(methods.exists('foo', 'bar', { field: 'foo' })).be.fulfilled(); + return should(methods.exists('foo', 'index', 'bar', { field: 'foo' })).be.fulfilled(); }); }); it('should register the filter in the global area in case of a "not exist" filter', function () { return methods.__with__({ - buildCurriedFunction: function (collection, field, operatorName, value, curriedFunctionName, roomId, not, inGlobals) { + buildCurriedFunction: function (index, collection, field, operatorName, value, curriedFunctionName, roomId, not, inGlobals) { should(inGlobals).be.true(); should(curriedFunctionName).startWith('not'); return { path: '' }; } })(function () { - return should(methods.exists('foo', 'bar', { field: 'foo' }, true)).be.fulfilled(); + return should(methods.exists('foo', 'index', 'bar', { field: 'foo' }, true)).be.fulfilled(); }); }); }); \ No newline at end of file diff --git a/test/api/dsl/methods/geoBoundingBox.test.js b/test/api/dsl/methods/geoBoundingBox.test.js index 4e8dc04704..a49bf475fa 100644 --- a/test/api/dsl/methods/geoBoundingBox.test.js +++ b/test/api/dsl/methods/geoBoundingBox.test.js @@ -10,6 +10,7 @@ require('should-promised'); describe('Test geoboundingbox method', function () { var roomId = 'roomId', + index = 'test', collection = 'collection', documentGrace = { firstName: 'Grace', @@ -67,27 +68,27 @@ describe('Test geoboundingbox method', function () { before(function () { methods.dsl.filtersTree = {}; - return methods.geoBoundingBox(roomId, collection, filterEngland) + return methods.geoBoundingBox(roomId, index, collection, filterEngland) .then(function () { - return methods.geoBoundingBox(roomId, collection, filterEngland2); + return methods.geoBoundingBox(roomId, index, collection, filterEngland2); }) .then(function () { - return methods.geoBoundingBox(roomId, collection, filterEngland3); + return methods.geoBoundingBox(roomId, index, collection, filterEngland3); }) .then(function () { - return methods.geoBoundingBox(roomId, collection, filterUSA); + return methods.geoBoundingBox(roomId, index, collection, filterUSA); }) .then(function () { - return methods.geoBoundingBox(roomId, collection, filterUSA2); + return methods.geoBoundingBox(roomId, index, collection, filterUSA2); }); }); it('should construct the filterTree object for the correct attribute', function () { should(methods.dsl.filtersTree).not.be.empty(); - should(methods.dsl.filtersTree[collection]).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields).not.be.empty(); - - should(methods.dsl.filtersTree[collection].fields.location).not.be.empty(); + should(methods.dsl.filtersTree[index]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.location).not.be.empty(); }); it('should construct the filterTree with correct curried function name', function () { @@ -95,26 +96,26 @@ describe('Test geoboundingbox method', function () { // because we have many times the same coord in filters, // we must have only three functions (one for filterEngland, and two for filterUSA) - should(Object.keys(methods.dsl.filtersTree[collection].fields.location)).have.length(3); - should(methods.dsl.filtersTree[collection].fields.location.locationgeoBoundingBoxgcmfj457fu10ffy7m4).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.location.locationgeoBoundingBoxj042p0j0phsc9wnc4v).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.location.locationgeoBoundingBoxc0x5c7zzzds7jw7zzz).not.be.empty(); + should(Object.keys(methods.dsl.filtersTree[index][collection].fields.location)).have.length(3); + should(methods.dsl.filtersTree[index][collection].fields.location.locationgeoBoundingBoxgcmfj457fu10ffy7m4).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.location.locationgeoBoundingBoxj042p0j0phsc9wnc4v).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.location.locationgeoBoundingBoxc0x5c7zzzds7jw7zzz).not.be.empty(); }); it('should construct the filterTree with correct room list', function () { var rooms; - rooms = methods.dsl.filtersTree[collection].fields.location.locationgeoBoundingBoxgcmfj457fu10ffy7m4.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.location.locationgeoBoundingBoxgcmfj457fu10ffy7m4.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.location.locationgeoBoundingBoxj042p0j0phsc9wnc4v.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.location.locationgeoBoundingBoxj042p0j0phsc9wnc4v.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.location.locationgeoBoundingBoxc0x5c7zzzds7jw7zzz.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.location.locationgeoBoundingBoxc0x5c7zzzds7jw7zzz.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); @@ -124,26 +125,26 @@ describe('Test geoboundingbox method', function () { var result; // test filterEngland - result = methods.dsl.filtersTree[collection].fields.location.locationgeoBoundingBoxgcmfj457fu10ffy7m4.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoBoundingBoxgcmfj457fu10ffy7m4.fn(documentGrace); should(result).be.exactly(false); - result = methods.dsl.filtersTree[collection].fields.location.locationgeoBoundingBoxgcmfj457fu10ffy7m4.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoBoundingBoxgcmfj457fu10ffy7m4.fn(documentAda); should(result).be.exactly(true); // test filterUSA - result = methods.dsl.filtersTree[collection].fields.location.locationgeoBoundingBoxj042p0j0phsc9wnc4v.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoBoundingBoxj042p0j0phsc9wnc4v.fn(documentGrace); should(result).be.exactly(false); - result = methods.dsl.filtersTree[collection].fields.location.locationgeoBoundingBoxj042p0j0phsc9wnc4v.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoBoundingBoxj042p0j0phsc9wnc4v.fn(documentAda); should(result).be.exactly(false); // test filterUSA2 - result = methods.dsl.filtersTree[collection].fields.location.locationgeoBoundingBoxc0x5c7zzzds7jw7zzz.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoBoundingBoxc0x5c7zzzds7jw7zzz.fn(documentGrace); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.location.locationgeoBoundingBoxc0x5c7zzzds7jw7zzz.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoBoundingBoxc0x5c7zzzds7jw7zzz.fn(documentAda); should(result).be.exactly(false); }); it('should return a rejected promise if an empty filter is provided', function () { - return should(methods.geoBoundingBox('foo', 'bar', {})).be.rejectedWith(BadRequestError, { message: 'Missing filter' }); + return should(methods.geoBoundingBox('foo', index, 'bar', {})).be.rejectedWith(BadRequestError, { message: 'Missing filter' }); }); it('should return a rejected promise if the geolocalisation filter is invalid', function () { @@ -156,14 +157,14 @@ describe('Test geoboundingbox method', function () { } }; - return should(methods.geoBoundingBox(roomId, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'Unable to parse coordinates' }); + return should(methods.geoBoundingBox(roomId, index, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'Unable to parse coordinates' }); }); it('should return a rejected promise if buildCurriedFunction fails', function () { return methods.__with__({ buildCurriedFunction: function () { return new InternalError('rejected'); } })(function () { - return should(methods.geoBoundingBox(roomId, collection, filterEngland)).be.rejectedWith('rejected'); + return should(methods.geoBoundingBox(roomId, index, collection, filterEngland)).be.rejectedWith('rejected'); }); }); }); \ No newline at end of file diff --git a/test/api/dsl/methods/geoDistance.test.js b/test/api/dsl/methods/geoDistance.test.js index 95d9cf520b..101dd88721 100644 --- a/test/api/dsl/methods/geoDistance.test.js +++ b/test/api/dsl/methods/geoDistance.test.js @@ -10,6 +10,7 @@ require('should-promised'); describe('Test geoDistance method', function () { var roomId = 'roomId', + index = 'test', collection = 'collection', document = { name: 'Zero', @@ -48,23 +49,24 @@ describe('Test geoDistance method', function () { before(function () { methods.dsl.filtersTree = {}; - return methods.geoDistance(roomId, collection, filterExact) + return methods.geoDistance(roomId, index, collection, filterExact) .then(function () { - return methods.geoDistance(roomId, collection, filterOK); + return methods.geoDistance(roomId, index, collection, filterOK); }) .then(function () { - return methods.geoDistance(roomId, collection, filterTooFar); + return methods.geoDistance(roomId, index, collection, filterTooFar); }) .then(function () { - return methods.geoDistance(roomId, collection, filterOkHumanReadable); + return methods.geoDistance(roomId, index, collection, filterOkHumanReadable); }); }); it('should construct the filterTree object for the correct attribute', function () { should(methods.dsl.filtersTree).not.be.empty(); - should(methods.dsl.filtersTree[collection]).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.location).not.be.empty(); + should(methods.dsl.filtersTree[index]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.location).not.be.empty(); }); it('should construct the filterTree with correct curried function name', function () { @@ -72,32 +74,32 @@ describe('Test geoDistance method', function () { // because we have many times the same coord in filters, // we must have only four functions - should(Object.keys(methods.dsl.filtersTree[collection].fields.location)).have.length(4); - should(methods.dsl.filtersTree[collection].fields.location.locationgeoDistancekpbxyzbpv111318).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.location.locationgeoDistancekpbxyzbpv111320).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.location.locationgeoDistancekpbxyzbpv111317).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.location['locationgeoDistancekpbxyzbpv111318.9999168']).not.be.empty(); + should(Object.keys(methods.dsl.filtersTree[index][collection].fields.location)).have.length(4); + should(methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistancekpbxyzbpv111318).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistancekpbxyzbpv111320).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistancekpbxyzbpv111317).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.location['locationgeoDistancekpbxyzbpv111318.9999168']).not.be.empty(); }); it('should construct the filterTree with correct room list', function () { var rooms; - rooms = methods.dsl.filtersTree[collection].fields.location.locationgeoDistancekpbxyzbpv111318.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistancekpbxyzbpv111318.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.location.locationgeoDistancekpbxyzbpv111320.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistancekpbxyzbpv111320.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.location.locationgeoDistancekpbxyzbpv111317.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistancekpbxyzbpv111317.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.location['locationgeoDistancekpbxyzbpv111318.9999168'].rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.location['locationgeoDistancekpbxyzbpv111318.9999168'].rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); @@ -107,25 +109,25 @@ describe('Test geoDistance method', function () { var result; // test exact - result = methods.dsl.filtersTree[collection].fields.location.locationgeoDistancekpbxyzbpv111318.fn(document); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistancekpbxyzbpv111318.fn(document); should(result).be.exactly(true); // test ok - result = methods.dsl.filtersTree[collection].fields.location.locationgeoDistancekpbxyzbpv111320.fn(document); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistancekpbxyzbpv111320.fn(document); should(result).be.exactly(true); // test too far - result = methods.dsl.filtersTree[collection].fields.location.locationgeoDistancekpbxyzbpv111317.fn(document); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistancekpbxyzbpv111317.fn(document); should(result).be.exactly(false); // test human readable distance - result = methods.dsl.filtersTree[collection].fields.location['locationgeoDistancekpbxyzbpv111318.9999168'].fn(document); + result = methods.dsl.filtersTree[index][collection].fields.location['locationgeoDistancekpbxyzbpv111318.9999168'].fn(document); should(result).be.exactly(true); }); it('should return a rejected promise if an empty filter is provided', function () { - return should(methods.geoDistance('foo', 'bar', {})).be.rejectedWith(BadRequestError, { message: 'Missing filter' }); + return should(methods.geoDistance('foo', index, 'bar', {})).be.rejectedWith(BadRequestError, { message: 'Missing filter' }); }); it('should return a rejected promise if the geolocalisation filter is invalid', function () { @@ -139,7 +141,7 @@ describe('Test geoDistance method', function () { distance: 123 }; - return should(methods.geoDistance(roomId, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'Unable to parse coordinates' }); + return should(methods.geoDistance(roomId, index, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'Unable to parse coordinates' }); }); it('should handle correctly the case when distance is the first filter member', function () { @@ -152,7 +154,7 @@ describe('Test geoDistance method', function () { } }; - return methods.geoDistance(roomId, collection, distanceFirstFilter); + return methods.geoDistance(roomId, index, collection, distanceFirstFilter); }); it('should handle correctly the case when the location is noted with the underscore notation', function () { @@ -169,7 +171,7 @@ describe('Test geoDistance method', function () { }; /* jshint camelcase: true */ - return methods.geoDistance(roomId, collection, underscoreFilter); + return methods.geoDistance(roomId, index, collection, underscoreFilter); }); it('should return a rejected promise if the distance filter parameter is missing', function () { @@ -181,7 +183,7 @@ describe('Test geoDistance method', function () { } }; - return should(methods.geoDistance(roomId, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'No distance given' }); + return should(methods.geoDistance(roomId, index, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'No distance given' }); }); it('should return a rejected promise if the location filter parameter is missing', function () { @@ -190,7 +192,7 @@ describe('Test geoDistance method', function () { distance: 123 }; - return should(methods.geoDistance(roomId, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'No location field given' }); + return should(methods.geoDistance(roomId, index, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'No location field given' }); }); it('should handle the not parameter', function () { @@ -203,7 +205,7 @@ describe('Test geoDistance method', function () { distance: 123 }; - return methods.geoDistance(roomId, collection, notFilter, true); + return methods.geoDistance(roomId, index, collection, notFilter, true); }); it('should return a rejected promise if the distance filter parameter is missing', function () { @@ -216,14 +218,14 @@ describe('Test geoDistance method', function () { distance: 'bad bad bad' }; - return should(methods.geoDistance(roomId, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'Unable to parse the distance filter parameter' }); + return should(methods.geoDistance(roomId, index, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'Unable to parse the distance filter parameter' }); }); it('should return a rejected promise if buildCurriedFunction fails', function () { return methods.__with__({ buildCurriedFunction: function () { return new InternalError('rejected'); } })(function () { - return should(methods.geoDistance(roomId, collection, filterOK)).be.rejectedWith('rejected'); + return should(methods.geoDistance(roomId, index, collection, filterOK)).be.rejectedWith('rejected'); }); }); }); \ No newline at end of file diff --git a/test/api/dsl/methods/geoDistanceRange.test.js b/test/api/dsl/methods/geoDistanceRange.test.js index 0a40c671b5..5443cb4a31 100644 --- a/test/api/dsl/methods/geoDistanceRange.test.js +++ b/test/api/dsl/methods/geoDistanceRange.test.js @@ -10,6 +10,7 @@ require('should-promised'); describe('Test geoDistanceRange method', function () { var roomId = 'roomId', + index = 'test', collection = 'collection', document = { name: 'Zero', @@ -35,7 +36,7 @@ describe('Test geoDistanceRange method', function () { filterOK = { location: { lat: 0, - lon: 1, + lon: 1 }, from: 111320, to: 111317 @@ -43,7 +44,7 @@ describe('Test geoDistanceRange method', function () { filterNOK = { location: { lat: 0, - lon: 1, + lon: 1 }, from: 1, to: 10 @@ -51,7 +52,7 @@ describe('Test geoDistanceRange method', function () { filterOkHumanReadable = { location: { lat: 0, - lon: 1, + lon: 1 }, from: '365 200 Ft', to: '365 220 Ft' @@ -67,29 +68,31 @@ describe('Test geoDistanceRange method', function () { before(function () { methods.dsl.filtersTree = {}; - return methods.geoDistanceRange(roomId, collection, filterOK) + return methods.geoDistanceRange(roomId, index, collection, filterOK) .then(function () { - return methods.geoDistanceRange(roomId, collection, filterNOK); + return methods.geoDistanceRange(roomId, index, collection, filterNOK); }) .then(function () { - return methods.geoDistanceRange(roomId, collection, filterOkHumanReadable); + return methods.geoDistanceRange(roomId, index, collection, filterOkHumanReadable); }) .then(function () { - return methods.geoDistanceRange(roomId, collection, filterEqual); + return methods.geoDistanceRange(roomId, index, collection, filterEqual); }); }); it('should construct the filterTree object for the correct attribute', function () { should(methods.dsl.filtersTree).not.be.empty(); - should(methods.dsl.filtersTree[collection]).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.location).not.be.empty(); + should(methods.dsl.filtersTree[index]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.location).not.be.empty(); }); it('should ', function () { should(methods.dsl.filtersTree).not.be.empty(); - should(methods.dsl.filtersTree[collection]).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.location).not.be.empty(); + should(methods.dsl.filtersTree[index]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.location).not.be.empty(); }); it('should construct the filterTree with correct curried function name', function () { @@ -97,32 +100,32 @@ describe('Test geoDistanceRange method', function () { // because we have many times the same coord in filters, // we must have only four functions - should(Object.keys(methods.dsl.filtersTree[collection].fields.location)).have.length(4); - should(methods.dsl.filtersTree[collection].fields.location.locationgeoDistanceRangekpbxyzbpv111320111317).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.location.locationgeoDistanceRangekpbxyzbpv110).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.location.locationgeoDistanceRangekpbxyzbpv111318111318).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.location['locationgeoDistanceRangekpbxyzbpv111312.96111319.05600000001']).not.be.empty(); + should(Object.keys(methods.dsl.filtersTree[index][collection].fields.location)).have.length(4); + should(methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistanceRangekpbxyzbpv111320111317).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistanceRangekpbxyzbpv110).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistanceRangekpbxyzbpv111318111318).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.location['locationgeoDistanceRangekpbxyzbpv111312.96111319.05600000001']).not.be.empty(); }); it('should construct the filterTree with correct room list', function () { var rooms; - rooms = methods.dsl.filtersTree[collection].fields.location.locationgeoDistanceRangekpbxyzbpv111320111317.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistanceRangekpbxyzbpv111320111317.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.location.locationgeoDistanceRangekpbxyzbpv110.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistanceRangekpbxyzbpv110.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.location.locationgeoDistanceRangekpbxyzbpv111318111318.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistanceRangekpbxyzbpv111318111318.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.location['locationgeoDistanceRangekpbxyzbpv111312.96111319.05600000001'].rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.location['locationgeoDistanceRangekpbxyzbpv111312.96111319.05600000001'].rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); @@ -132,35 +135,35 @@ describe('Test geoDistanceRange method', function () { var result; // test ok - result = methods.dsl.filtersTree[collection].fields.location.locationgeoDistanceRangekpbxyzbpv111320111317.fn(document); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistanceRangekpbxyzbpv111320111317.fn(document); should(result).be.exactly(true); // test not ok - result = methods.dsl.filtersTree[collection].fields.location.locationgeoDistanceRangekpbxyzbpv110.fn(document); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistanceRangekpbxyzbpv110.fn(document); should(result).be.exactly(false); // test human readable distance - result = methods.dsl.filtersTree[collection].fields.location.locationgeoDistanceRangekpbxyzbpv111318111318.fn(document); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistanceRangekpbxyzbpv111318111318.fn(document); should(result).be.exactly(true); // test from == to - result = methods.dsl.filtersTree[collection].fields.location['locationgeoDistanceRangekpbxyzbpv111312.96111319.05600000001'].fn(document); + result = methods.dsl.filtersTree[index][collection].fields.location['locationgeoDistanceRangekpbxyzbpv111312.96111319.05600000001'].fn(document); should(result).be.exactly(true); }); it('should return false if no lat or lon members exists for the location member of the document', function () { // test ok - result = methods.dsl.filtersTree[collection].fields.location.locationgeoDistanceRangekpbxyzbpv111320111317.fn(documentBad); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistanceRangekpbxyzbpv111320111317.fn(documentBad); should(result).be.exactly(false); - result = methods.dsl.filtersTree[collection].fields.location.locationgeoDistanceRangekpbxyzbpv111320111317.fn(documentBadLon); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistanceRangekpbxyzbpv111320111317.fn(documentBadLon); should(result).be.exactly(false); - result = methods.dsl.filtersTree[collection].fields.location.locationgeoDistanceRangekpbxyzbpv111320111317.fn(documentBadLat); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoDistanceRangekpbxyzbpv111320111317.fn(documentBadLat); should(result).be.exactly(false); }); it('should return a rejected promise if an empty filter is provided', function () { - return should(methods.geoDistanceRange('foo', 'bar', {})).be.rejectedWith(BadRequestError, { message: 'Missing filter' }); + return should(methods.geoDistanceRange('foo', index, 'bar', {})).be.rejectedWith(BadRequestError, { message: 'Missing filter' }); }); it('should handle correctly the case when from and to comes first, before the location', function () { @@ -178,7 +181,7 @@ describe('Test geoDistanceRange method', function () { }; /* jshint camelcase: true */ - return methods.geoDistanceRange(roomId, collection, underscoreFilter); + return methods.geoDistanceRange(roomId, index, collection, underscoreFilter); }); it('should handle the not parameter', function () { @@ -192,7 +195,7 @@ describe('Test geoDistanceRange method', function () { } }; - return methods.geoDistanceRange(roomId, collection, notFilter, true); + return methods.geoDistanceRange(roomId, index, collection, notFilter, true); }); it('should return a rejected promise if the geolocalisation filter is invalid', function () { @@ -207,7 +210,7 @@ describe('Test geoDistanceRange method', function () { to: 133 }; - return should(methods.geoDistanceRange(roomId, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'Unable to parse coordinates' }); + return should(methods.geoDistanceRange(roomId, index, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'Unable to parse coordinates' }); }); it('should return a rejected promise if the from filter parameter is missing', function () { @@ -220,7 +223,7 @@ describe('Test geoDistanceRange method', function () { to: 123 }; - return should(methods.geoDistanceRange(roomId, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'No from parameter given' }); + return should(methods.geoDistanceRange(roomId, index, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'No from parameter given' }); }); it('should return a rejected promise if the to filter parameter is missing', function () { @@ -233,7 +236,7 @@ describe('Test geoDistanceRange method', function () { from: 123 }; - return should(methods.geoDistanceRange(roomId, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'No to parameter given' }); + return should(methods.geoDistanceRange(roomId, index, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'No to parameter given' }); }); it('should return a rejected promise if the location filter parameter is missing', function () { @@ -243,7 +246,7 @@ describe('Test geoDistanceRange method', function () { to: 456 }; - return should(methods.geoDistanceRange(roomId, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'No location field given' }); + return should(methods.geoDistanceRange(roomId, index, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'No location field given' }); }); it('should return a rejected promise if the distance filter parameter is missing', function () { @@ -257,14 +260,14 @@ describe('Test geoDistanceRange method', function () { to: 'bad bad bad' }; - return should(methods.geoDistanceRange(roomId, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'Unable to parse the distance filter parameter' }); + return should(methods.geoDistanceRange(roomId, index, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'Unable to parse the distance filter parameter' }); }); it('should return a rejected promise if buildCurriedFunction fails', function () { return methods.__with__({ buildCurriedFunction: function () { return new InternalError('rejected'); } })(function () { - return should(methods.geoDistanceRange(roomId, collection, filterOK)).be.rejectedWith('rejected'); + return should(methods.geoDistanceRange(roomId, index, collection, filterOK)).be.rejectedWith('rejected'); }); }); }); \ No newline at end of file diff --git a/test/api/dsl/methods/geoPolygon.test.js b/test/api/dsl/methods/geoPolygon.test.js index 0e2b2a2613..8cc35d71cf 100755 --- a/test/api/dsl/methods/geoPolygon.test.js +++ b/test/api/dsl/methods/geoPolygon.test.js @@ -10,6 +10,7 @@ require('should-promised'); describe('Test geoPolygon method', function () { var roomId = 'roomId', + index = 'test', collection = 'collection', document = { name: 'Zero', @@ -50,20 +51,21 @@ describe('Test geoPolygon method', function () { before(function () { methods.dsl.filtersTree = {}; - methods.geoPolygon(roomId, collection, filterExact) + methods.geoPolygon(roomId, index, collection, filterExact) .then(function () { - return methods.geoPolygon(roomId, collection, filterLimit); + return methods.geoPolygon(roomId, index, collection, filterLimit); }) .then(function () { - return methods.geoPolygon(roomId, collection, filterOutside); + return methods.geoPolygon(roomId, index, collection, filterOutside); }); }); it('should construct the filterTree object for the correct attribute', function () { should(methods.dsl.filtersTree).not.be.empty(); - should(methods.dsl.filtersTree[collection]).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.location).not.be.empty(); + should(methods.dsl.filtersTree[index]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.location).not.be.empty(); }); it('should construct the filterTree with correct curried function name', function () { @@ -71,26 +73,26 @@ describe('Test geoPolygon method', function () { // because we have many times the same coord in filters, // we must have only four functions - should(Object.keys(methods.dsl.filtersTree[collection].fields.location)).have.length(3); - should(methods.dsl.filtersTree[collection].fields.location.locationgeoPolygonkpbdqcbnts00twy01mebpm9npc67zz631zyd).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.location.locationgeoPolygonkpbxyzbpvs00twy01mebpvxypcr7zzzzzzzz).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.location.locationgeoPolygons1zbfk3yns1zyd63zws1zned3z8s1z0gs3y0).not.be.empty(); + should(Object.keys(methods.dsl.filtersTree[index][collection].fields.location)).have.length(3); + should(methods.dsl.filtersTree[index][collection].fields.location.locationgeoPolygonkpbdqcbnts00twy01mebpm9npc67zz631zyd).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.location.locationgeoPolygonkpbxyzbpvs00twy01mebpvxypcr7zzzzzzzz).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.location.locationgeoPolygons1zbfk3yns1zyd63zws1zned3z8s1z0gs3y0).not.be.empty(); }); it('should construct the filterTree with correct room list', function () { var rooms; - rooms = methods.dsl.filtersTree[collection].fields.location.locationgeoPolygonkpbdqcbnts00twy01mebpm9npc67zz631zyd.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.location.locationgeoPolygonkpbdqcbnts00twy01mebpm9npc67zz631zyd.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.location.locationgeoPolygonkpbxyzbpvs00twy01mebpvxypcr7zzzzzzzz.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.location.locationgeoPolygonkpbxyzbpvs00twy01mebpvxypcr7zzzzzzzz.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.location.locationgeoPolygons1zbfk3yns1zyd63zws1zned3z8s1z0gs3y0.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.location.locationgeoPolygons1zbfk3yns1zyd63zws1zned3z8s1z0gs3y0.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); @@ -101,21 +103,21 @@ describe('Test geoPolygon method', function () { var result; // test exact - result = methods.dsl.filtersTree[collection].fields.location.locationgeoPolygonkpbdqcbnts00twy01mebpm9npc67zz631zyd.fn(document); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoPolygonkpbdqcbnts00twy01mebpm9npc67zz631zyd.fn(document); should(result).be.exactly(true); // test outside - result = methods.dsl.filtersTree[collection].fields.location.locationgeoPolygonkpbxyzbpvs00twy01mebpvxypcr7zzzzzzzz.fn(document); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoPolygonkpbxyzbpvs00twy01mebpvxypcr7zzzzzzzz.fn(document); should(result).be.exactly(true); // test on limit - result = methods.dsl.filtersTree[collection].fields.location.locationgeoPolygons1zbfk3yns1zyd63zws1zned3z8s1z0gs3y0.fn(document); + result = methods.dsl.filtersTree[index][collection].fields.location.locationgeoPolygons1zbfk3yns1zyd63zws1zned3z8s1z0gs3y0.fn(document); should(result).be.exactly(false); }); it('should return a rejected promise if an empty filter is provided', function () { - return should(methods.geoPolygon('foo', 'bar', {})).be.rejectedWith(BadRequestError, { message: 'Missing filter' }); + return should(methods.geoPolygon('foo', index, 'bar', {})).be.rejectedWith(BadRequestError, { message: 'Missing filter' }); }); it('should return a rejected promise if the geolocalisation filter is invalid', function () { @@ -129,7 +131,7 @@ describe('Test geoPolygon method', function () { distance: 123 }; - return should(methods.geoPolygon(roomId, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'No point list found' }); + return should(methods.geoPolygon(roomId, index, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'No point list found' }); }); it('should return a rejected promise if the location filter parameter is missing', function () { @@ -138,11 +140,11 @@ describe('Test geoPolygon method', function () { distance: 123 }; - return should(methods.geoPolygon(roomId, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'No point list found' }); + return should(methods.geoPolygon(roomId, index, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'No point list found' }); }); it('should handle the not parameter', function () { - return methods.geoPolygon(roomId, collection, filterExact, true); + return methods.geoPolygon(roomId, index, collection, filterExact, true); }); it('should return a rejected promise if the location filter parameter does not contain a points member', function () { @@ -154,7 +156,7 @@ describe('Test geoPolygon method', function () { } }; - return should(methods.geoPolygon(roomId, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'No point list found' }); + return should(methods.geoPolygon(roomId, index, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'No point list found' }); }); it('should return a rejected promise if the location filter parameter contain a points filter with less than 3 points', function () { @@ -168,7 +170,7 @@ describe('Test geoPolygon method', function () { } }; - return should(methods.geoPolygon(roomId, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'A polygon must have at least 3 points' }); + return should(methods.geoPolygon(roomId, index, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'A polygon must have at least 3 points' }); }); it('should return a rejected promise if the location filter parameter contain a points filter wich is not an array', function () { @@ -179,14 +181,14 @@ describe('Test geoPolygon method', function () { } }; - return should(methods.geoPolygon(roomId, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'A polygon must be in array format' }); + return should(methods.geoPolygon(roomId, index, collection, invalidFilter)).be.rejectedWith(BadRequestError, { message: 'A polygon must be in array format' }); }); it('should return a rejected promise if buildCurriedFunction fails', function () { return methods.__with__({ buildCurriedFunction: function () { return new InternalError('rejected'); } })(function () { - return should(methods.geoPolygon(roomId, collection, filterExact)).be.rejectedWith('rejected'); + return should(methods.geoPolygon(roomId, index, collection, filterExact)).be.rejectedWith('rejected'); }); }); }); \ No newline at end of file diff --git a/test/api/dsl/methods/getFormattedFilters.test.js b/test/api/dsl/methods/getFormattedFilters.test.js index f226dde7c2..99fd1b4544 100644 --- a/test/api/dsl/methods/getFormattedFilters.test.js +++ b/test/api/dsl/methods/getFormattedFilters.test.js @@ -15,11 +15,11 @@ describe('Test: dsl.getFormattedFilters method', function () { }); it('should return a rejected promise if the provided filter is empty', function () { - return should(getFormattedFilters('roomId', 'collection', {})).be.rejectedWith(BadRequestError, { message: 'Filters can\'t be empty' }); + return should(getFormattedFilters('roomId', 'index', 'collection', {})).be.rejectedWith(BadRequestError, { message: 'Filters can\'t be empty' }); }); it('should return a rejected promise if a filter refers to an unknown method name', function () { - return should(getFormattedFilters('roomId', 'collection', { foo: 'bar'})).be.rejectedWith(BadRequestError, { message: 'Function foo doesn\'t exist' }); + return should(getFormattedFilters('roomId', 'index', 'collection', { foo: 'bar'})).be.rejectedWith(BadRequestError, { message: 'Function foo doesn\'t exist' }); }); it('should return a resolved promise containing 1 formatted filter', function (done) { @@ -30,14 +30,14 @@ describe('Test: dsl.getFormattedFilters method', function () { } }; - getFormattedFilters('roomId', 'collection', filter) + getFormattedFilters('roomId', 'index', 'collection', filter) .then(function (formattedFilter) { - should.exist(formattedFilter['collection.foo.existsfoo']); - should(formattedFilter['collection.foo.existsfoo']).be.an.Object(); - should.exist(formattedFilter['collection.foo.existsfoo'].rooms); - should(formattedFilter['collection.foo.existsfoo'].rooms).be.an.Array().and.match(['roomId']); - should(formattedFilter['collection.foo.existsfoo'].rooms.length).eql(1); - should.exist(formattedFilter['collection.foo.existsfoo'].fn); + should.exist(formattedFilter['index.collection.foo.existsfoo']); + should(formattedFilter['index.collection.foo.existsfoo']).be.an.Object(); + should.exist(formattedFilter['index.collection.foo.existsfoo'].rooms); + should(formattedFilter['index.collection.foo.existsfoo'].rooms).be.an.Array().and.match(['roomId']); + should(formattedFilter['index.collection.foo.existsfoo'].rooms.length).eql(1); + should.exist(formattedFilter['index.collection.foo.existsfoo'].fn); done(); }) .catch(function (error) { @@ -52,21 +52,21 @@ describe('Test: dsl.getFormattedFilters method', function () { { exists: { field: 'bar' } } ]; - getFormattedFilters('roomId', 'collection', filters) + getFormattedFilters('roomId', 'index', 'collection', filters) .then(function (formattedFilter) { - should.exist(formattedFilter['collection.foo.existsfoo']); - should(formattedFilter['collection.foo.existsfoo']).be.an.Object(); - should.exist(formattedFilter['collection.foo.existsfoo'].rooms); - should(formattedFilter['collection.foo.existsfoo'].rooms).be.an.Array().and.match(['roomId']); - should(formattedFilter['collection.foo.existsfoo'].rooms.length).eql(1); - should.exist(formattedFilter['collection.foo.existsfoo'].fn); - - should.exist(formattedFilter['collection.bar.existsbar']); - should(formattedFilter['collection.bar.existsbar']).be.an.Object(); - should.exist(formattedFilter['collection.bar.existsbar'].rooms); - should(formattedFilter['collection.bar.existsbar'].rooms).be.an.Array().and.match(['roomId']); - should(formattedFilter['collection.bar.existsbar'].rooms.length).eql(1); - should.exist(formattedFilter['collection.bar.existsbar'].fn); + should.exist(formattedFilter['index.collection.foo.existsfoo']); + should(formattedFilter['index.collection.foo.existsfoo']).be.an.Object(); + should.exist(formattedFilter['index.collection.foo.existsfoo'].rooms); + should(formattedFilter['index.collection.foo.existsfoo'].rooms).be.an.Array().and.match(['roomId']); + should(formattedFilter['index.collection.foo.existsfoo'].rooms.length).eql(1); + should.exist(formattedFilter['index.collection.foo.existsfoo'].fn); + + should.exist(formattedFilter['index.collection.bar.existsbar']); + should(formattedFilter['index.collection.bar.existsbar']).be.an.Object(); + should.exist(formattedFilter['index.collection.bar.existsbar'].rooms); + should(formattedFilter['index.collection.bar.existsbar'].rooms).be.an.Array().and.match(['roomId']); + should(formattedFilter['index.collection.bar.existsbar'].rooms.length).eql(1); + should.exist(formattedFilter['index.collection.bar.existsbar'].fn); done(); }) @@ -83,21 +83,21 @@ describe('Test: dsl.getFormattedFilters method', function () { { exists: { field: 'bar' } } ]; - getFormattedFilters('roomId', 'collection', filters) + getFormattedFilters('roomId', 'index', 'collection', filters) .then(function (formattedFilter) { - should.exist(formattedFilter['collection.foo.existsfoo']); - should(formattedFilter['collection.foo.existsfoo']).be.an.Object(); - should.exist(formattedFilter['collection.foo.existsfoo'].rooms); - should(formattedFilter['collection.foo.existsfoo'].rooms).be.an.Array().and.match(['roomId']); - should(formattedFilter['collection.foo.existsfoo'].rooms.length).eql(1); - should.exist(formattedFilter['collection.foo.existsfoo'].fn); - - should.exist(formattedFilter['collection.bar.existsbar']); - should(formattedFilter['collection.bar.existsbar']).be.an.Object(); - should.exist(formattedFilter['collection.bar.existsbar'].rooms); - should(formattedFilter['collection.bar.existsbar'].rooms).be.an.Array().and.match(['roomId']); - should(formattedFilter['collection.bar.existsbar'].rooms.length).eql(1); - should.exist(formattedFilter['collection.bar.existsbar'].fn); + should.exist(formattedFilter['index.collection.foo.existsfoo']); + should(formattedFilter['index.collection.foo.existsfoo']).be.an.Object(); + should.exist(formattedFilter['index.collection.foo.existsfoo'].rooms); + should(formattedFilter['index.collection.foo.existsfoo'].rooms).be.an.Array().and.match(['roomId']); + should(formattedFilter['index.collection.foo.existsfoo'].rooms.length).eql(1); + should.exist(formattedFilter['index.collection.foo.existsfoo'].fn); + + should.exist(formattedFilter['index.collection.bar.existsbar']); + should(formattedFilter['index.collection.bar.existsbar']).be.an.Object(); + should.exist(formattedFilter['index.collection.bar.existsbar'].rooms); + should(formattedFilter['index.collection.bar.existsbar'].rooms).be.an.Array().and.match(['roomId']); + should(formattedFilter['index.collection.bar.existsbar'].rooms.length).eql(1); + should.exist(formattedFilter['index.collection.bar.existsbar'].fn); done(); }) @@ -114,14 +114,14 @@ describe('Test: dsl.getFormattedFilters method', function () { } }; - getFormattedFilters('roomId', 'collection', filter, true) + getFormattedFilters('roomId', 'index', 'collection', filter, true) .then(function (formattedFilter) { - should.exist(formattedFilter['collection.foo.notexistsfoo']); - should(formattedFilter['collection.foo.notexistsfoo']).be.an.Object(); - should.exist(formattedFilter['collection.foo.notexistsfoo'].rooms); - should(formattedFilter['collection.foo.notexistsfoo'].rooms).be.an.Array().and.match(['roomId']); - should(formattedFilter['collection.foo.notexistsfoo'].rooms.length).eql(1); - should.exist(formattedFilter['collection.foo.notexistsfoo'].fn); + should.exist(formattedFilter['index.collection.foo.notexistsfoo']); + should(formattedFilter['index.collection.foo.notexistsfoo']).be.an.Object(); + should.exist(formattedFilter['index.collection.foo.notexistsfoo'].rooms); + should(formattedFilter['index.collection.foo.notexistsfoo'].rooms).be.an.Array().and.match(['roomId']); + should(formattedFilter['index.collection.foo.notexistsfoo'].rooms.length).eql(1); + should.exist(formattedFilter['index.collection.foo.notexistsfoo'].fn); done(); }) .catch(function (error) { @@ -139,6 +139,6 @@ describe('Test: dsl.getFormattedFilters method', function () { methods.exists = function () { return Promise.reject(new Error('rejected')); }; - return should(getFormattedFilters('roomId', 'collection', filter)).be.rejectedWith('rejected'); + return should(getFormattedFilters('roomId', 'index', 'collection', filter)).be.rejectedWith('rejected'); }); }); diff --git a/test/api/dsl/methods/ids.test.js b/test/api/dsl/methods/ids.test.js index 6d268ee69e..b274d051d5 100644 --- a/test/api/dsl/methods/ids.test.js +++ b/test/api/dsl/methods/ids.test.js @@ -12,6 +12,7 @@ describe('Test ids method', function () { var roomIdMatch = 'roomIdMatch', roomIdNot = 'roomIdNotMatch', + index = 'test', collection = 'collection', documentGrace = { _id: 'idGrace', @@ -30,31 +31,31 @@ describe('Test ids method', function () { before(function () { methods.dsl.filtersTree = {}; - return methods.ids(roomIdMatch, collection, filter, false) + return methods.ids(roomIdMatch, index, collection, filter, false) .then(function() { - return methods.ids(roomIdNot, collection, filter, true); + return methods.ids(roomIdNot, index, collection, filter, true); }); }); it('should construct the filterTree object for the correct attribute', function () { should(methods.dsl.filtersTree).not.be.empty(); - should(methods.dsl.filtersTree[collection]).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields).not.be.empty(); - - should(methods.dsl.filtersTree[collection].fields._id).not.be.empty(); + should(methods.dsl.filtersTree[index]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields._id).not.be.empty(); }); it('should construct the filterTree with correct curried function name', function () { /* jshint camelcase:false */ - should(methods.dsl.filtersTree[collection].fields._id.ids_ididGrace).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields._id.notids_ididGrace).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields._id.ids_ididGrace).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields._id.notids_ididGrace).not.be.empty(); }); it('should construct the filterTree with correct room list', function () { /* jshint camelcase:false */ var - rooms = methods.dsl.filtersTree[collection].fields._id.ids_ididGrace.rooms, - roomsNot = methods.dsl.filtersTree[collection].fields._id.notids_ididGrace.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields._id.ids_ididGrace.rooms, + roomsNot = methods.dsl.filtersTree[index][collection].fields._id.notids_ididGrace.rooms; should(rooms).be.an.Array(); should(roomsNot).be.an.Array(); @@ -69,40 +70,40 @@ describe('Test ids method', function () { it('should construct the filterTree with correct functions ids', function () { /* jshint camelcase:false */ var - resultMatch = methods.dsl.filtersTree[collection].fields._id.ids_ididGrace.fn(documentGrace), - resultNotMatch = methods.dsl.filtersTree[collection].fields._id.ids_ididGrace.fn(documentAda); + resultMatch = methods.dsl.filtersTree[index][collection].fields._id.ids_ididGrace.fn(documentGrace), + resultNotMatch = methods.dsl.filtersTree[index][collection].fields._id.ids_ididGrace.fn(documentAda); should(resultMatch).be.exactly(true); should(resultNotMatch).be.exactly(false); - resultMatch = methods.dsl.filtersTree[collection].fields._id.notids_ididGrace.fn(documentAda); - resultNotMatch = methods.dsl.filtersTree[collection].fields._id.notids_ididGrace.fn(documentGrace); + resultMatch = methods.dsl.filtersTree[index][collection].fields._id.notids_ididGrace.fn(documentAda); + resultNotMatch = methods.dsl.filtersTree[index][collection].fields._id.notids_ididGrace.fn(documentGrace); should(resultMatch).be.exactly(true); should(resultNotMatch).be.exactly(false); }); it('should reject a promise if the filter is empty', function () { - return should(methods.ids(roomIdMatch, collection, {})).be.rejectedWith(BadRequestError); + return should(methods.ids(roomIdMatch, index, collection, {})).be.rejectedWith(BadRequestError); }); it('should reject a promise if the filter has no "values"', function () { - return should(methods.ids(roomIdMatch, collection, {foo: 'bar'})).be.rejectedWith(BadRequestError); + return should(methods.ids(roomIdMatch, index, collection, {foo: 'bar'})).be.rejectedWith(BadRequestError); }); it('should reject a promise if "values" is not an array', function () { - return should(methods.ids(roomIdMatch, collection, {values: 'toto'})).be.rejectedWith(BadRequestError); + return should(methods.ids(roomIdMatch, index, collection, {values: 'toto'})).be.rejectedWith(BadRequestError); }); it('should reject a promise if the filter has empty "values"', function () { - return should(methods.ids(roomIdMatch, collection, {values: []})).be.rejectedWith(BadRequestError); + return should(methods.ids(roomIdMatch, index, collection, {values: []})).be.rejectedWith(BadRequestError); }); it('should return a rejected promise if buildCurriedFunction fails', function () { return methods.__with__({ buildCurriedFunction: function () { return new InternalError('rejected'); } })(function () { - return should(methods.ids(roomIdMatch, collection, filter, false)).be.rejectedWith('rejected'); + return should(methods.ids(roomIdMatch, index, collection, filter, false)).be.rejectedWith('rejected'); }); }); }); \ No newline at end of file diff --git a/test/api/dsl/methods/missing.test.js b/test/api/dsl/methods/missing.test.js index 7835d6f890..6e8388e72a 100644 --- a/test/api/dsl/methods/missing.test.js +++ b/test/api/dsl/methods/missing.test.js @@ -11,6 +11,7 @@ describe('Test missing method', function () { var roomId = 'roomId', + index = 'index', collection = 'collection', documentGrace = { firstName: 'Grace', @@ -26,25 +27,26 @@ describe('Test missing method', function () { before(function () { methods.dsl.filtersTree = {}; - return methods.missing(roomId, collection, filter, false); + return methods.missing(roomId, index, collection, filter, false); }); it('should construct the filterTree object for the correct attribute', function () { should(methods.dsl.filtersTree).not.be.empty(); - should(methods.dsl.filtersTree[collection]).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.lastName).not.be.empty(); + should(methods.dsl.filtersTree[index]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.lastName).not.be.empty(); }); it('should construct the filterTree with correct curried function name', function () { - should(methods.dsl.filtersTree[collection].fields.lastName.missinglastName).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.lastName.missinglastName).not.be.empty(); }); it('should construct the filterTree with correct room list', function () { var rooms; // Test gt from filterGrace - rooms = methods.dsl.filtersTree[collection].fields.lastName.missinglastName.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.lastName.missinglastName.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); @@ -53,49 +55,49 @@ describe('Test missing method', function () { it('should construct the filterTree with correct functions missing', function () { var result; - result = methods.dsl.filtersTree[collection].fields.lastName.missinglastName.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.lastName.missinglastName.fn(documentAda); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.lastName.missinglastName.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.lastName.missinglastName.fn(documentGrace); should(result).be.exactly(false); }); it('should return a rejected promise if the filter argument is empty', function () { - return should(methods.missing('foo', 'bar', {}, false)).be.rejectedWith(BadRequestError, { message: 'A filter can\'t be empty' }); + return should(methods.missing('foo', index, 'bar', {}, false)).be.rejectedWith(BadRequestError, { message: 'A filter can\'t be empty' }); }); it('should return a rejected promise if the filter argument is invalid', function () { - return should(methods.missing('foo', 'bar', { foo: 'bar' }, false)).be.rejectedWith(BadRequestError, { message: 'Filter \'missing\' must contains \'field\' attribute' }); + return should(methods.missing('foo', index, 'bar', { foo: 'bar' }, false)).be.rejectedWith(BadRequestError, { message: 'Filter \'missing\' must contains \'field\' attribute' }); }); it('should return a rejected promise if buildCurriedFunction fails', function () { return methods.__with__({ buildCurriedFunction: function () { return new InternalError('rejected'); } })(function () { - return should(methods.missing('foo', 'bar', { field: 'foo' }, false)); + return should(methods.missing('foo', index, 'bar', { field: 'foo' }, false)); }); }); it('should register the filter in the lcao area in case of a "missing" filter', function () { return methods.__with__({ - buildCurriedFunction: function (collection, field, operatorName, value, curriedFunctionName, roomId, not, inGlobals) { + buildCurriedFunction: function (index, collection, field, operatorName, value, curriedFunctionName, roomId, not, inGlobals) { should(inGlobals).be.false(); should(curriedFunctionName).not.startWith('not'); return { path: '' }; } })(function () { - return should(methods.missing('foo', 'bar', { field: 'foo' }, false)).be.fulfilled(); + return should(methods.missing('foo', index, 'bar', { field: 'foo' }, false)).be.fulfilled(); }); }); it('should register the filter in the global area in case of a "not missing" filter', function () { return methods.__with__({ - buildCurriedFunction: function (collection, field, operatorName, value, curriedFunctionName, roomId, not, inGlobals) { + buildCurriedFunction: function (index, collection, field, operatorName, value, curriedFunctionName, roomId, not, inGlobals) { should(inGlobals).be.true(); should(curriedFunctionName).startWith('not'); return { path: '' }; } })(function () { - return should(methods.missing('foo', 'bar', { field: 'foo' }, true)).be.fulfilled(); + return should(methods.missing('foo', index, 'bar', { field: 'foo' }, true)).be.fulfilled(); }); }); }); \ No newline at end of file diff --git a/test/api/dsl/methods/must.test.js b/test/api/dsl/methods/must.test.js index d743ced8c4..bfc0f26538 100644 --- a/test/api/dsl/methods/must.test.js +++ b/test/api/dsl/methods/must.test.js @@ -18,10 +18,10 @@ describe('Test: dsl.must method', function () { }); it('should return an embedded object containing the result of getFormattedFilters', function () { - return should(methods.must('resolve', {}, {}, false)).be.fulfilledWith({and: 'resolved'}); + return should(methods.must('resolve', 'index', {}, {}, false)).be.fulfilledWith({and: 'resolved'}); }); it('should return a rejected promise if getFormattedFilters fails', function () { - return should(methods.must('rejected', {}, {}, false)).be.rejectedWith('rejected'); + return should(methods.must('rejected', 'index', {}, {}, false)).be.rejectedWith('rejected'); }); }); diff --git a/test/api/dsl/methods/mustNot.test.js b/test/api/dsl/methods/mustNot.test.js index 3a861d33a2..38bde9b5ba 100644 --- a/test/api/dsl/methods/mustNot.test.js +++ b/test/api/dsl/methods/mustNot.test.js @@ -7,17 +7,17 @@ require('should-promised'); describe('Test: dsl.mustNot method', function () { before(function () { - methods.must = function (roomId, collection, filters, not) { + methods.must = function (roomId, index, collection, filters, not) { should(roomId).be.exactly(not); }; }); it('should pass an inverted "not" argument to the must function', function () { - methods.mustNot(true, {}, {}, false); - methods.mustNot(false, {}, {}, true); + methods.mustNot(true, 'index', {}, {}, false); + methods.mustNot(false, 'index', {}, {}, true); }); it('should default default the "not" argument to true', function () { - methods.mustNot(true, {}, {}); + methods.mustNot(true, 'index', {}, {}); }); }); diff --git a/test/api/dsl/methods/not.test.js b/test/api/dsl/methods/not.test.js index 92a8b03f56..0bde7def00 100644 --- a/test/api/dsl/methods/not.test.js +++ b/test/api/dsl/methods/not.test.js @@ -5,6 +5,7 @@ var describe('Test not method', function () { var roomId = 'roomId', + index = 'index', collection = 'collection', documentGrace = { firstName: 'Grace', @@ -24,26 +25,26 @@ describe('Test not method', function () { before(function () { methods.dsl.filtersTree = {}; - return methods.not(roomId, collection, filter); + return methods.not(roomId, index, collection, filter); }); it('should construct the filterTree object for the correct attribute', function () { should(methods.dsl.filtersTree).not.be.empty(); - should(methods.dsl.filtersTree[collection]).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields).not.be.empty(); - - should(methods.dsl.filtersTree[collection].fields.city).not.be.empty(); + should(methods.dsl.filtersTree[index]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.city).not.be.empty(); }); it('should construct the filterTree with correct curried function name', function () { - should(methods.dsl.filtersTree[collection].fields.city.nottermcityLondon).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.city.nottermcityLondon).not.be.empty(); }); it('should construct the filterTree with correct room list', function () { var rooms; // Test gt from filterGrace - rooms = methods.dsl.filtersTree[collection].fields.city.nottermcityLondon.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.city.nottermcityLondon.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); @@ -52,18 +53,18 @@ describe('Test not method', function () { it('should construct the filterTree with correct functions', function () { var result; - result = methods.dsl.filtersTree[collection].fields.city.nottermcityLondon.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.city.nottermcityLondon.fn(documentGrace); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.city.nottermcityLondon.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.city.nottermcityLondon.fn(documentAda); should(result).be.exactly(false); }); it('should pass an inverted "not" argument to the must function', function () { - methods.must = function (roomId, collection, filters, not) { + methods.must = function (roomId, index, collection, filters, not) { should(roomId).be.exactly(not); }; - methods.not(true, {}, {}, false); - methods.not(false, {}, {}, true); + methods.not(true, index, {}, {}, false); + methods.not(false, index, {}, {}, true); }); }); diff --git a/test/api/dsl/methods/or.test.js b/test/api/dsl/methods/or.test.js index 735152a00a..5461cd51ef 100644 --- a/test/api/dsl/methods/or.test.js +++ b/test/api/dsl/methods/or.test.js @@ -1,6 +1,7 @@ var should = require('should'), - methods = require.main.require('lib/api/dsl/methods'), + rewire = require('rewire'), + methods = rewire('../../../../lib/api/dsl/methods'), BadRequestError = require.main.require('lib/api/core/errors/badRequestError'); require('should-promised'); @@ -9,6 +10,7 @@ describe('Test or method', function () { var roomId = 'roomId', + index = 'index', collection = 'collection', documentGrace = { firstName: 'Grace', @@ -38,46 +40,46 @@ describe('Test or method', function () { before(function () { methods.dsl.filtersTree = {}; - return methods.or(roomId, collection, filter) + return methods.or(roomId, index, collection, filter) .then(function () { - return methods.or(roomId, collection, filter, true); + return methods.or(roomId, index, collection, filter, true); }); }); it('should construct the filterTree object for the correct attribute', function () { should(methods.dsl.filtersTree).not.be.empty(); - should(methods.dsl.filtersTree[collection]).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields).not.be.empty(); - - should(methods.dsl.filtersTree[collection].fields.city).not.be.empty(); + should(methods.dsl.filtersTree[index]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.city).not.be.empty(); }); it('should construct the filterTree with correct curried function name', function () { - should(methods.dsl.filtersTree[collection].fields.city.termcityNYC).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.city.termcityLondon).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.city.nottermcityNYC).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.city.nottermcityLondon).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.city.termcityNYC).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.city.termcityLondon).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.city.nottermcityNYC).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.city.nottermcityLondon).not.be.empty(); }); it('should construct the filterTree with correct room list', function () { var rooms; - rooms = methods.dsl.filtersTree[collection].fields.city.termcityNYC.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.city.termcityNYC.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.city.termcityLondon.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.city.termcityLondon.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.city.nottermcityNYC.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.city.nottermcityNYC.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); - rooms = methods.dsl.filtersTree[collection].fields.city.nottermcityLondon.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.city.nottermcityLondon.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomId); @@ -86,27 +88,35 @@ describe('Test or method', function () { it('should construct the filterTree with correct functions', function () { var result; - result = methods.dsl.filtersTree[collection].fields.city.termcityNYC.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.city.termcityNYC.fn(documentGrace); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.city.termcityNYC.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.city.termcityNYC.fn(documentAda); should(result).be.exactly(false); - result = methods.dsl.filtersTree[collection].fields.city.termcityLondon.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.city.termcityLondon.fn(documentGrace); should(result).be.exactly(false); - result = methods.dsl.filtersTree[collection].fields.city.termcityLondon.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.city.termcityLondon.fn(documentAda); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.city.nottermcityNYC.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.city.nottermcityNYC.fn(documentGrace); should(result).be.exactly(false); - result = methods.dsl.filtersTree[collection].fields.city.nottermcityNYC.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.city.nottermcityNYC.fn(documentAda); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.city.nottermcityLondon.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.city.nottermcityLondon.fn(documentGrace); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.city.nottermcityLondon.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.city.nottermcityLondon.fn(documentAda); should(result).be.exactly(false); }); + it('should return a rejected promise if getFormattedFilters fails', function () { + return methods.__with__({ + getFormattedFilters: function () { return Promise.reject(new Error('rejected')); } + })(function () { + return should(methods.or(roomId, index, collection, filter)).be.rejectedWith('rejected'); + }); + }); + it('should reject an error if the filter OR is not an array', function () { return should(methods.or(roomId, collection, {})).be.rejectedWith(BadRequestError); }); diff --git a/test/api/dsl/methods/range.test.js b/test/api/dsl/methods/range.test.js index ef0405c7d3..7253d60725 100644 --- a/test/api/dsl/methods/range.test.js +++ b/test/api/dsl/methods/range.test.js @@ -15,6 +15,7 @@ describe('Test range method', function () { roomIdFilterAll = 'roomIdAll', roomIdFilterNobody = 'roomIdNobody', + index = 'index', collection = 'collection', documentGrace = { firstName: 'Grace', @@ -49,72 +50,72 @@ describe('Test range method', function () { before(function () { methods.dsl.filtersTree = {}; - return methods.range(roomIdFilterGrace, collection, filterGrace) + return methods.range(roomIdFilterGrace, index, collection, filterGrace) .then(function () { - return methods.range(roomIdFilterAda, collection, filterAda); + return methods.range(roomIdFilterAda, index, collection, filterAda); }) .then(function () { - return methods.range(roomIdFilterAll, collection, filterAll); + return methods.range(roomIdFilterAll, index, collection, filterAll); }) .then(function () { - return methods.range(roomIdFilterNobody, collection, filterAll, true); + return methods.range(roomIdFilterNobody, index, collection, filterAll, true); }); }); it('should construct the filterTree object for the correct attribute', function () { should(methods.dsl.filtersTree).not.be.empty(); - should(methods.dsl.filtersTree[collection]).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields).not.be.empty(); - - should(methods.dsl.filtersTree[collection].fields.age).not.be.empty(); + should(methods.dsl.filtersTree[index]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.age).not.be.empty(); }); it('should construct the filterTree with correct curried function name', function () { - should(methods.dsl.filtersTree[collection].fields.age.rangeagegt36).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.age.rangeagelte85).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.age.rangeagegte36).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.age.rangeagelt85).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.age.notrangeagegte36).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.age.notrangeagelte85).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.age.rangeagegt36).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.age.rangeagelte85).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.age.rangeagegte36).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.age.rangeagelt85).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.age.notrangeagegte36).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.age.notrangeagelte85).not.be.empty(); }); it('should construct the filterTree with correct room list', function () { var rooms; // Test gt from filterGrace - rooms = methods.dsl.filtersTree[collection].fields.age.rangeagegt36.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.age.rangeagegt36.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomIdFilterGrace); // Test lte from filterGrace and filterAll - rooms = methods.dsl.filtersTree[collection].fields.age.rangeagelte85.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.age.rangeagelte85.rooms; should(rooms).be.an.Array(); should(rooms).have.length(2); should(rooms).containEql(roomIdFilterGrace); should(rooms).containEql(roomIdFilterAll); // Test gte from filterAda and filterAll - rooms = methods.dsl.filtersTree[collection].fields.age.rangeagegte36.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.age.rangeagegte36.rooms; should(rooms).be.an.Array(); should(rooms).have.length(2); should(rooms).containEql(roomIdFilterAda); should(rooms).containEql(roomIdFilterAll); // Test lt from filterAda - rooms = methods.dsl.filtersTree[collection].fields.age.rangeagelt85.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.age.rangeagelt85.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomIdFilterAda); // Test not gte from negative filterAll - rooms = methods.dsl.filtersTree[collection].fields.age.notrangeagegte36.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.age.notrangeagegte36.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomIdFilterNobody); // Test not lte from negative filterAll - rooms = methods.dsl.filtersTree[collection].fields.age.notrangeagelte85.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.age.notrangeagelte85.rooms; should(rooms).be.an.Array(); should(rooms).have.length(1); should(rooms[0]).be.exactly(roomIdFilterNobody); @@ -123,46 +124,46 @@ describe('Test range method', function () { it('should construct the filterTree with correct functions range', function () { var result; - result = methods.dsl.filtersTree[collection].fields.age.rangeagegt36.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.age.rangeagegt36.fn(documentGrace); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.age.rangeagegt36.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.age.rangeagegt36.fn(documentAda); should(result).be.exactly(false); - result = methods.dsl.filtersTree[collection].fields.age.rangeagelte85.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.age.rangeagelte85.fn(documentGrace); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.age.rangeagelte85.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.age.rangeagelte85.fn(documentAda); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.age.rangeagegte36.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.age.rangeagegte36.fn(documentGrace); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.age.rangeagegte36.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.age.rangeagegte36.fn(documentAda); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.age.rangeagelt85.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.age.rangeagelt85.fn(documentGrace); should(result).be.exactly(false); - result = methods.dsl.filtersTree[collection].fields.age.rangeagelt85.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.age.rangeagelt85.fn(documentAda); should(result).be.exactly(true); - result = methods.dsl.filtersTree[collection].fields.age.notrangeagegte36.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.age.notrangeagegte36.fn(documentGrace); should(result).be.exactly(false); - result = methods.dsl.filtersTree[collection].fields.age.notrangeagegte36.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.age.notrangeagegte36.fn(documentAda); should(result).be.exactly(false); - result = methods.dsl.filtersTree[collection].fields.age.notrangeagelte85.fn(documentGrace); + result = methods.dsl.filtersTree[index][collection].fields.age.notrangeagelte85.fn(documentGrace); should(result).be.exactly(false); - result = methods.dsl.filtersTree[collection].fields.age.notrangeagelte85.fn(documentAda); + result = methods.dsl.filtersTree[index][collection].fields.age.notrangeagelte85.fn(documentAda); should(result).be.exactly(false); }); it('should return a rejected promise if the filter is empty', function () { - return should(methods.range(roomIdFilterGrace, collection, {})).be.rejectedWith(BadRequestError, { message: 'A filter can\'t be empty' }); + return should(methods.range(roomIdFilterGrace, index, collection, {})).be.rejectedWith(BadRequestError, { message: 'A filter can\'t be empty' }); }); it('should return a rejected promise if buildCurriedFunction fails', function () { return methods.__with__({ buildCurriedFunction: function () { return new InternalError('rejected'); } })(function () { - return should(methods.range(roomIdFilterGrace, collection, filterGrace)).be.rejectedWith('rejected'); + return should(methods.range(roomIdFilterGrace, index, collection, filterGrace)).be.rejectedWith('rejected'); }); }); }); \ No newline at end of file diff --git a/test/api/dsl/methods/should.test.js b/test/api/dsl/methods/should.test.js index 54ec2ae62c..05f3bfed92 100644 --- a/test/api/dsl/methods/should.test.js +++ b/test/api/dsl/methods/should.test.js @@ -1,10 +1,21 @@ var should = require('should'), - methods = require.main.require('lib/api/dsl/methods'); + rewire = require('rewire'), + methods = rewire('../../../../lib/api/dsl/methods'); require('should-promised'); describe('Test: dsl.should method', function () { + before(function () { + methods.__set__('getFormattedFilters', function (roomId) { + if (roomId === 'resolve') { + return Promise.resolve('resolved'); + } + else { + return Promise.reject(new Error('rejected')); + } + }); + }); it('should call the function "AND" in case of a should-not filter', function () { var andIsCalled = false; @@ -12,7 +23,7 @@ describe('Test: dsl.should method', function () { andIsCalled = true; }; - methods.should('roomId', {}, {}, true); + methods.should('roomId', 'index', {}, {}, true); should(andIsCalled).be.exactly(true); }); @@ -22,7 +33,7 @@ describe('Test: dsl.should method', function () { orIsCalled = true; }; - methods.should('roomId', {}, {}, false); + methods.should('roomId', 'index', {}, {}, false); should(orIsCalled).be.exactly(true); }); }); diff --git a/test/api/dsl/methods/term.test.js b/test/api/dsl/methods/term.test.js index 8de0f6873e..615d25a62a 100644 --- a/test/api/dsl/methods/term.test.js +++ b/test/api/dsl/methods/term.test.js @@ -7,6 +7,7 @@ describe('Test term method', function () { var roomIdMatch = 'roomIdMatch', roomIdNot = 'roomIdNotMatch', + index = 'index', collection = 'collection', documentGrace = { firstName: 'Grace', @@ -23,29 +24,29 @@ describe('Test term method', function () { before(function () { methods.dsl.filtersTree = {}; - return methods.term(roomIdMatch, collection, filter) + return methods.term(roomIdMatch, index, collection, filter) .then(function() { - return methods.term(roomIdNot, collection, filter, true); + return methods.term(roomIdNot, index, collection, filter, true); }); }); it('should construct the filterTree object for the correct attribute', function () { should(methods.dsl.filtersTree).not.be.empty(); - should(methods.dsl.filtersTree[collection]).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields).not.be.empty(); - - should(methods.dsl.filtersTree[collection].fields.firstName).not.be.empty(); + should(methods.dsl.filtersTree[index]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.firstName).not.be.empty(); }); it('should construct the filterTree with correct curried function name', function () { - should(methods.dsl.filtersTree[collection].fields.firstName.termfirstNameGrace).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.firstName.nottermfirstNameGrace).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.firstName.termfirstNameGrace).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.firstName.nottermfirstNameGrace).not.be.empty(); }); it('should construct the filterTree with correct room list', function () { var - rooms = methods.dsl.filtersTree[collection].fields.firstName.termfirstNameGrace.rooms, - roomsNot = methods.dsl.filtersTree[collection].fields.firstName.nottermfirstNameGrace.rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.firstName.termfirstNameGrace.rooms, + roomsNot = methods.dsl.filtersTree[index][collection].fields.firstName.nottermfirstNameGrace.rooms; should(rooms).be.an.Array(); should(roomsNot).be.an.Array(); @@ -59,14 +60,14 @@ describe('Test term method', function () { it('should construct the filterTree with correct functions term', function () { var - resultMatch = methods.dsl.filtersTree[collection].fields.firstName.termfirstNameGrace.fn(documentGrace), - resultNotMatch = methods.dsl.filtersTree[collection].fields.firstName.termfirstNameGrace.fn(documentAda); + resultMatch = methods.dsl.filtersTree[index][collection].fields.firstName.termfirstNameGrace.fn(documentGrace), + resultNotMatch = methods.dsl.filtersTree[index][collection].fields.firstName.termfirstNameGrace.fn(documentAda); should(resultMatch).be.exactly(true); should(resultNotMatch).be.exactly(false); - resultMatch = methods.dsl.filtersTree[collection].fields.firstName.nottermfirstNameGrace.fn(documentAda); - resultNotMatch = methods.dsl.filtersTree[collection].fields.firstName.nottermfirstNameGrace.fn(documentGrace); + resultMatch = methods.dsl.filtersTree[index][collection].fields.firstName.nottermfirstNameGrace.fn(documentAda); + resultNotMatch = methods.dsl.filtersTree[index][collection].fields.firstName.nottermfirstNameGrace.fn(documentGrace); should(resultMatch).be.exactly(true); should(resultNotMatch).be.exactly(false); diff --git a/test/api/dsl/methods/termFunction.test.js b/test/api/dsl/methods/termFunction.test.js index dfc089cc47..ba9e2a3e5c 100644 --- a/test/api/dsl/methods/termFunction.test.js +++ b/test/api/dsl/methods/termFunction.test.js @@ -25,7 +25,7 @@ describe('Test: dsl.termFunction method', function () { foo: 'bar' }; - return should(termFunction('terms', 'roomId', 'collection', filter)).be.rejectedWith(BadRequestError, { message: 'Filter terms must contains an array' }); + return should(termFunction('terms', 'roomId', 'index', 'collection', filter)).be.rejectedWith(BadRequestError, { message: 'Filter terms must contains an array' }); }); it('should create a valid "term" filter', function (done) { @@ -34,11 +34,11 @@ describe('Test: dsl.termFunction method', function () { foo: 'bar' }; - termFunction('term', 'roomId', 'collection', filter) + termFunction('term', 'roomId', 'index', 'collection', filter) .then(function (formattedFilter) { - should.exist(formattedFilter['collection.foo.termfoobar']); - should(formattedFilter['collection.foo.termfoobar'].rooms).be.an.Array().and.match(['roomId']); - should(formattedFilter['collection.foo.termfoobar'].fn).be.a.Function(); + should.exist(formattedFilter['index.collection.foo.termfoobar']); + should(formattedFilter['index.collection.foo.termfoobar'].rooms).be.an.Array().and.match(['roomId']); + should(formattedFilter['index.collection.foo.termfoobar'].fn).be.a.Function(); done(); }) .catch(function (error) { @@ -52,11 +52,11 @@ describe('Test: dsl.termFunction method', function () { foo: ['bar', 'baz'] }; - termFunction('terms', 'roomId', 'collection', filter) + termFunction('terms', 'roomId', 'index', 'collection', filter) .then(function (formattedFilter) { - should.exist(formattedFilter['collection.foo.termsfoobar,baz']); - should(formattedFilter['collection.foo.termsfoobar,baz'].rooms).be.an.Array().and.match(['roomId']); - should(formattedFilter['collection.foo.termsfoobar,baz'].fn).be.a.Function(); + should.exist(formattedFilter['index.collection.foo.termsfoobar,baz']); + should(formattedFilter['index.collection.foo.termsfoobar,baz'].rooms).be.an.Array().and.match(['roomId']); + should(formattedFilter['index.collection.foo.termsfoobar,baz'].fn).be.a.Function(); done(); }) .catch(function (error) { @@ -70,11 +70,11 @@ describe('Test: dsl.termFunction method', function () { foo: 'bar' }; - termFunction('term', 'roomId', 'collection', filter, true) + termFunction('term', 'roomId', 'index', 'collection', filter, true) .then(function (formattedFilter) { - should.exist(formattedFilter['collection.foo.nottermfoobar']); - should(formattedFilter['collection.foo.nottermfoobar'].rooms).be.an.Array().and.match(['roomId']); - should(formattedFilter['collection.foo.nottermfoobar'].fn).be.a.Function(); + should.exist(formattedFilter['index.collection.foo.nottermfoobar']); + should(formattedFilter['index.collection.foo.nottermfoobar'].rooms).be.an.Array().and.match(['roomId']); + should(formattedFilter['index.collection.foo.nottermfoobar'].fn).be.a.Function(); done(); }) .catch(function (error) { @@ -88,11 +88,11 @@ describe('Test: dsl.termFunction method', function () { foo: ['bar', 'baz'] }; - termFunction('terms', 'roomId', 'collection', filter, true) + termFunction('terms', 'roomId', 'index', 'collection', filter, true) .then(function (formattedFilter) { - should.exist(formattedFilter['collection.foo.nottermsfoobar,baz']); - should(formattedFilter['collection.foo.nottermsfoobar,baz'].rooms).be.an.Array().and.match(['roomId']); - should(formattedFilter['collection.foo.nottermsfoobar,baz'].fn).be.a.Function(); + should.exist(formattedFilter['index.collection.foo.nottermsfoobar,baz']); + should(formattedFilter['index.collection.foo.nottermsfoobar,baz'].rooms).be.an.Array().and.match(['roomId']); + should(formattedFilter['index.collection.foo.nottermsfoobar,baz'].fn).be.a.Function(); done(); }) .catch(function (error) { @@ -109,7 +109,7 @@ describe('Test: dsl.termFunction method', function () { return methods.__with__({ buildCurriedFunction: function () { return new InternalError('rejected'); } })(function () { - return should(termFunction('terms', 'roomId', 'collection', filter)).be.rejectedWith('rejected'); + return should(termFunction('terms', 'roomId', 'index', 'collection', filter)).be.rejectedWith('rejected'); }); }); }); diff --git a/test/api/dsl/methods/terms.test.js b/test/api/dsl/methods/terms.test.js index dd56c363a8..d1853cf34b 100644 --- a/test/api/dsl/methods/terms.test.js +++ b/test/api/dsl/methods/terms.test.js @@ -7,6 +7,7 @@ describe('Test terms method', function () { var roomIdMatch = 'roomIdMatch', roomIdNot = 'roomIdNotMatch', + index = 'index', collection = 'collection', documentGrace = { firstName: 'Grace', @@ -23,29 +24,29 @@ describe('Test terms method', function () { before(function () { methods.dsl.filtersTree = {}; - return methods.terms(roomIdMatch, collection, filter, false) + return methods.terms(roomIdMatch, index, collection, filter, false) .then(function() { - return methods.terms(roomIdNot, collection, filter, true); + return methods.terms(roomIdNot, index, collection, filter, true); }); }); it('should construct the filterTree object for the correct attribute', function () { should(methods.dsl.filtersTree).not.be.empty(); - should(methods.dsl.filtersTree[collection]).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields).not.be.empty(); - - should(methods.dsl.filtersTree[collection].fields.firstName).not.be.empty(); + should(methods.dsl.filtersTree[index]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection]).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.firstName).not.be.empty(); }); it('should construct the filterTree with correct curried function name', function () { - should(methods.dsl.filtersTree[collection].fields.firstName['termsfirstNameGrace,Jean']).not.be.empty(); - should(methods.dsl.filtersTree[collection].fields.firstName['nottermsfirstNameGrace,Jean']).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.firstName['termsfirstNameGrace,Jean']).not.be.empty(); + should(methods.dsl.filtersTree[index][collection].fields.firstName['nottermsfirstNameGrace,Jean']).not.be.empty(); }); it('should construct the filterTree with correct room list', function () { var - rooms = methods.dsl.filtersTree[collection].fields.firstName['termsfirstNameGrace,Jean'].rooms, - roomsNot = methods.dsl.filtersTree[collection].fields.firstName['nottermsfirstNameGrace,Jean'].rooms; + rooms = methods.dsl.filtersTree[index][collection].fields.firstName['termsfirstNameGrace,Jean'].rooms, + roomsNot = methods.dsl.filtersTree[index][collection].fields.firstName['nottermsfirstNameGrace,Jean'].rooms; should(rooms).be.an.Array(); should(roomsNot).be.an.Array(); @@ -59,14 +60,14 @@ describe('Test terms method', function () { it('should construct the filterTree with correct functions terms', function () { var - resultMatch = methods.dsl.filtersTree[collection].fields.firstName['termsfirstNameGrace,Jean'].fn(documentGrace), - resultNotMatch = methods.dsl.filtersTree[collection].fields.firstName['termsfirstNameGrace,Jean'].fn(documentAda); + resultMatch = methods.dsl.filtersTree[index][collection].fields.firstName['termsfirstNameGrace,Jean'].fn(documentGrace), + resultNotMatch = methods.dsl.filtersTree[index][collection].fields.firstName['termsfirstNameGrace,Jean'].fn(documentAda); should(resultMatch).be.exactly(true); should(resultNotMatch).be.exactly(false); - resultMatch = methods.dsl.filtersTree[collection].fields.firstName['nottermsfirstNameGrace,Jean'].fn(documentAda); - resultNotMatch = methods.dsl.filtersTree[collection].fields.firstName['nottermsfirstNameGrace,Jean'].fn(documentGrace); + resultMatch = methods.dsl.filtersTree[index][collection].fields.firstName['nottermsfirstNameGrace,Jean'].fn(documentAda); + resultNotMatch = methods.dsl.filtersTree[index][collection].fields.firstName['nottermsfirstNameGrace,Jean'].fn(documentGrace); should(resultMatch).be.exactly(true); should(resultNotMatch).be.exactly(false); diff --git a/test/api/kuzzle.test.js b/test/api/kuzzle.test.js index 1dca274eca..ad9bc2a70f 100644 --- a/test/api/kuzzle.test.js +++ b/test/api/kuzzle.test.js @@ -1,4 +1,5 @@ var + rc = require('rc'), should = require('should'), Kuzzle = require.main.require('lib/api/Kuzzle'); @@ -29,4 +30,107 @@ describe('Test kuzzle constructor', function () { kuzzle.emit('event', {}); }); + + describe('#cleanDb', () => { + it('should clean database when environment variable LIKE_A_VIRGIN is set to 1', function (done) { + + var + hasDeletedIndexes = false, + hasCreatedIndex = false; + + process.env.LIKE_A_VIRGIN = 1; + kuzzle.isServer = true; + kuzzle.services.list = { + writeEngine: {} + }; + + kuzzle.pluginsManager = { + trigger: function(event, data) { + should(event).be.exactly('cleanDb:done'); + should(data).be.exactly('Reset done: Kuzzle is now like a virgin, touched for the very first time !'); + } + }; + + kuzzle.services.list.writeEngine.deleteIndexes = function() { + hasDeletedIndexes = true; + return Promise.resolve(); + }; + + kuzzle.services.list.writeEngine.createIndex = function() { + hasCreatedIndex = true; + return Promise.resolve(); + }; + + kuzzle.cleanDb() + .then(() => { + should(hasDeletedIndexes).be.exactly(true); + should(hasCreatedIndex).be.exactly(true); + done(); + }) + .catch(error => done(error)); + }); + + it('should log an error if elasticsearch fail when cleaning database', function () { + + process.env.LIKE_A_VIRGIN = 1; + kuzzle.isServer = true; + kuzzle.services.list = { + writeEngine: {} + }; + + kuzzle.pluginsManager = { + trigger: function(event, data) { + should(event).be.exactly('cleanDb:error'); + should(data).be.exactly('Oops... something really bad happened during reset...'); + } + }; + + kuzzle.services.list.writeEngine.deleteIndexes = function() { + return Promise.reject(); + }; + + + kuzzle.services.list.writeEngine.createIndex = function() { + return Promise.reject(); + }; + + should(kuzzle.cleanDb()).be.fulfilled(); + }); + + it('should not clean database when environment variable LIKE_A_VIRGIN is not set to 1', function (done) { + var + hasDeletedIndexes = false, + hasCreatedIndex = false; + + + process.env.LIKE_A_VIRGIN = undefined; + kuzzle.isServer = true; + kuzzle.services.list = { + writeEngine: {} + }; + + kuzzle.services.list.writeEngine.deleteIndexes = function() { + hasDeletedIndexes = true; + return Promise.reject(); + }; + + kuzzle.services.list.writeEngine.createIndex = function() { + hasCreatedIndex = true; + return Promise.reject(); + }; + + + kuzzle.cleanDb() + .then(() => { + should(hasDeletedIndexes).be.exactly(false); + should(hasCreatedIndex).be.exactly(false); + done(); + }) + .catch(error => done(error)); + }); + }); + + describe('#prepareDb', () => { + + }); }); \ No newline at end of file diff --git a/test/services/implementations/elasticsearch.test.js b/test/services/implementations/elasticsearch.test.js index 5a4cc0ee08..e3b7e4d974 100644 --- a/test/services/implementations/elasticsearch.test.js +++ b/test/services/implementations/elasticsearch.test.js @@ -8,11 +8,15 @@ var require('should-promised'); + describe('Test: ElasticSearch service', function () { var - kuzzle = {}, + kuzzle = { + indexes: {} + }, + index = '%test', collection = 'unit-tests-elasticsearch', - createdDocumentId, + createdDocumentId = 'id-test', elasticsearch, engineType = 'readEngine', requestObject, @@ -39,41 +43,34 @@ describe('Test: ElasticSearch service', function () { } }; - beforeEach(function () { - kuzzle.config = new Config(params); - requestObject = new RequestObject({ - controller: 'write', - action: 'create', - requestId: 'foo', - collection: collection, - body: documentAda - }); + before(function () { + kuzzle.config = new Config(params); elasticsearch = new ES(kuzzle, {service: engineType}); - should(elasticsearch.init()).be.exactly(elasticsearch); + elasticsearch.client = { + indices: {}, + cat: {} + }; }); - after(function (done) { - /* - We catch a rejected promise because one should be thrown if all tests succeed, - has there isn't any collection left to delete. + beforeEach(function () { + requestObject = new RequestObject({ + controller: 'write', + action: 'create', + requestId: 'foo', + collection: collection, + index: index, + body: documentAda + }); - This hook is here only to ensure we clean up after tests if a test fails. - */ - elasticsearch.deleteCollection(requestObject) - .then(function () { - done(); - }) - .catch(function () { - done(); - }); + kuzzle.indexes[index] = [collection]; }); // init - it('should initialize properly', function () { + it('should initialize properly', function (done) { should(elasticsearch.init()).be.exactly(elasticsearch); - should(elasticsearch.client).not.be.null(); + done(); }); // cleanData @@ -88,7 +85,7 @@ describe('Test: ElasticSearch service', function () { should(preparedData.type).be.exactly(requestObject.collection); should(preparedData.id).be.exactly(requestObject.data._id); should(preparedData._id).be.undefined(); - should(preparedData.index).be.exactly(kuzzle.config.readEngine.index); + should(preparedData.index).be.exactly(requestObject.index); // we expect all properties expect _id to be carried over the new data object Object.keys(requestObject.data).forEach(function (member) { @@ -102,74 +99,125 @@ describe('Test: ElasticSearch service', function () { it('should be able to search documents', function (done) { var ret; + elasticsearch.client.search = function(data) { + should(data.body).be.exactly(filter); + + return Promise.resolve({hits: { total: 0, hits: []}}); + }; + requestObject.data.body = filter; ret = elasticsearch.search(requestObject); should(ret).be.a.Promise(); ret .then(function(result) { - should(result.error).not.be.undefined().and.be.null(); should(result.data).not.be.undefined().and.not.be.null(); should(result.data.hits).not.be.undefined(); should(result.data.hits.total).be.exactly(0); should(result.data.hits.hits).be.an.Array(); done(); }) - .catch(function (error) { - done(error); - }); + .catch(error => done(error)); }); it('should return a rejected promise if a search fails', function () { + + elasticsearch.client.search = function(data) { + should(data.body).not.be.exactly(filter); + + return Promise.reject(new Error()); + }; + return should(elasticsearch.search(requestObject)).be.rejected(); }); // create it('should allow creating documents', function (done) { - var ret = elasticsearch.create(requestObject); + var + ret; + + kuzzle.indexes = {}; + + elasticsearch.client.create = function(data) { + should(data.index).be.exactly(index); + should(data.type).be.exactly(collection); + should(data.body).be.exactly(documentAda); + + return Promise.resolve({}); + }; + + ret = elasticsearch.create(requestObject); should(ret).be.a.Promise(); ret .then(function (result) { - should(result._type).be.exactly(collection); - should(result._id).not.be.undefined().and.be.a.String(); - should(result.created).be.true(); - createdDocumentId = result._id; + should(kuzzle.indexes).be.an.instanceOf(Object).and.have.property(index, [collection]); done(); }) - .catch(function (error) { - done(error); - }); + .catch(error => done(error)); + }); + it('should reject the create promise if elasticsearch throws an error', function () { + var + ret; + + elasticsearch.client.create = function(data) { + return Promise.reject(new Error()); + }; + + return should(elasticsearch.create(requestObject)).be.rejected(); }); // createOrUpdate it('should support createOrUpdate capability', function (done) { var ret; - requestObject.data.id = createdDocumentId; + kuzzle.indexes = {}; + + elasticsearch.client.index = function(data) { + should(data.index).be.exactly(index); + should(data.type).be.exactly(collection); + should(data.body).be.exactly(documentAda); + should(data.id).be.exactly(createdDocumentId); + + return Promise.resolve({}); + }; + + requestObject.data._id = createdDocumentId; ret = elasticsearch.createOrUpdate(requestObject); should(ret).be.a.Promise(); ret .then(function (result) { - should(result._type).be.exactly(collection); - should(result._id).be.exactly(requestObject.data.id); - should(result.created).be.false(); - should(result._version).be.eql(2); - createdDocumentId = result._id; + should(kuzzle.indexes).be.an.instanceOf(Object).and.have.property(index, [collection]); done(); }) - .catch(function (error) { - done(error); - }); + .catch(error => done(error)); + }); + it('should reject the createOrUpdate promise if elasticsearch throws an error', function () { + var ret; + + elasticsearch.client.index = function(data) { + return Promise.reject(new Error()); + }; + + requestObject.data._id = createdDocumentId; + ret = elasticsearch.createOrUpdate(requestObject); + + return should(ret).be.rejected(); }); // get it('should allow getting a single document', function (done) { var ret; + elasticsearch.client.get = function(data) { + should(data.id).be.exactly(createdDocumentId); + + return Promise.resolve({}); + }; + delete requestObject.data.body; requestObject.data._id = createdDocumentId; @@ -178,24 +226,50 @@ describe('Test: ElasticSearch service', function () { should(ret).be.a.Promise(); ret - .then(function (result) { - should(result.data).not.be.undefined().and.be.an.Object(); - should(result.data._id).be.exactly(createdDocumentId); - should(result.data.found).be.true(); - should(result.data._source).match(documentAda); + .then(result => { + should(kuzzle.indexes).be.an.instanceOf(Object).and.have.property(index, [collection]); done(); }) - .catch(function (error) { - done(error); - }); + .catch(error => done(error)); }); + + // mget it('should return a rejected promise if getting a single document fails', function () { + + elasticsearch.client.get = function(data) { + should(data.id).be.undefined(); + + return Promise.reject(new Error()); + }; + + return should(elasticsearch.get(requestObject)).be.rejected(); }); - // mget + it('should allow getting multiples documents', function () { + + elasticsearch.client.mget = function(data) { + should(data.body.ids).be.an.Array(); + + return Promise.resolve(new Error()); + }; + + delete requestObject.data.body; + requestObject.data = { body: {ids: [1, 2, 3] } }; + + return should(elasticsearch.mget(requestObject)).be.fulfilled(); + }); + it('should return a rejected promise if getting some multiple documents fails', function () { + elasticsearch.client.mget = function(data) { + should(data.body.ids).be.undefined(); + + return Promise.reject(new Error()); + }; + + requestObject.data.body = {}; + return should(elasticsearch.mget(requestObject)).be.rejected(); }); @@ -203,64 +277,117 @@ describe('Test: ElasticSearch service', function () { it('should allow counting documents using a provided filter', function (done) { var ret; + elasticsearch.client.count = function(data) { + should(data.body).have.keys(); + + return Promise.resolve({}); + }; + requestObject.data.body = {}; ret = elasticsearch.count(requestObject); should(ret).be.a.Promise(); ret .then(function(result) { - should(result.data).not.be.undefined(); - should(result.data.body).be.an.Object().and.match({}); - should(result.data.count).be.a.Number(); done(); }) - .catch(function (error) { - done(error); - }); + .catch(error => done(error)); }); it('should allow counting objects using a query', function (done) { var ret; - delete requestObject.data.body; - requestObject.data.filter = { match: {firstName: 'Ada'}}; + elasticsearch.client.count = function(data) { + should(data.body).be.an.instanceOf(Object).and.have.property('query', {foo: 'bar'}); + + return Promise.resolve({}); + }; + + requestObject.data.body = {}; + requestObject.data.query = {foo: 'bar'}; + ret = elasticsearch.count(requestObject); should(ret).be.a.Promise(); ret .then(function(result) { - should(result.data).not.be.undefined(); - should(result.data.filter).be.an.Object().and.match({}); - should(result.data.count).be.a.Number(); done(); }) - .catch(function (error) { - done(error); - }); + .catch(error => done(error)); }); it('should return a rejected promise if the count fails', function () { + + elasticsearch.client.count = function(data) { + return Promise.reject(new Error()); + }; + + requestObject.data.body = {}; + requestObject.data.query = {foo: 'bar'}; + return should(elasticsearch.count(requestObject)).be.rejected(); }); // update - it('should allow to update a document', function () { + it('should allow to update a document', function (done) { + var ret; + + kuzzle.indexes = {}; + + elasticsearch.client.update = function(data) { + should(data.body.doc).be.exactly(documentAda); + should(data.id).be.exactly(createdDocumentId); + + return Promise.resolve({}); + }; + requestObject.data._id = createdDocumentId; - return should(elasticsearch.update(requestObject)).be.fulfilled(); + + ret = elasticsearch.update(requestObject); + should(ret).be.a.Promise(); + + ret + .then(function(result) { + should(kuzzle.indexes).be.an.instanceOf(Object).and.have.property(index, [collection]); + done(); + }) + .catch(error => done(error)); }); it('should return a rejected promise if an update fails', function () { + + elasticsearch.client.update = function(data) { + should(data.id).be.undefined(); + + return Promise.reject(new Error()); + }; + return should(elasticsearch.update(requestObject)).be.rejected(); }); // delete it('should allow to delete a document', function () { + + elasticsearch.client.delete = function(data) { + should(data.id).be.exactly(createdDocumentId); + + return Promise.resolve({}); + }; + delete requestObject.data.body; requestObject.data._id = createdDocumentId; + return should(elasticsearch.delete(requestObject)).be.fulfilled(); }); it('should return a rejected promise if a delete fails', function () { + + elasticsearch.client.delete = function(data) { + should(data.id).be.undefined(); + + return Promise.reject(new Error()); + }; + return should(elasticsearch.delete(requestObject)).be.rejected(); }); @@ -269,17 +396,20 @@ describe('Test: ElasticSearch service', function () { delete requestObject.data.body; requestObject.data.filter = { term: {firstName: 'no way any document can be returned with this filter'}}; + elasticsearch.client.search = function(data, callback) { + should(data.query).be.exactly(requestObject.data.query); + + callback(null, {hits: {hits: [], total:0}}); + }; + elasticsearch.deleteByQuery(requestObject) .then(function (result) { // Ugly line in order to spot a random bug on this unit test - console.log(result); - should(result.ids).not.be.undefined().and.be.an.Array(); - should(result.ids.length).be.exactly(0); + should(result.data.ids).not.be.undefined().and.be.an.Array(); + should(result.data.ids.length).be.exactly(0); done(); }) - .catch(function (error) { - done(error); - }); + .catch(error => done(error)); }); it('should allow to delete documents using a provided filter', function (done) { @@ -312,8 +442,8 @@ describe('Test: ElasticSearch service', function () { elasticsearch.deleteByQuery(requestObject) .then(function (result) { try { - should(result.ids).not.be.undefined().and.be.an.Array(); - should(result.ids).match(mockupIds); + should(result.data.ids).not.be.undefined().and.be.an.Array(); + should(result.data.ids).match(mockupIds); done(); } catch (e) { @@ -327,6 +457,11 @@ describe('Test: ElasticSearch service', function () { }); it('should return a rejected promise if the delete by query fails because of a bad filter', function () { + + elasticsearch.client.search = function(data, callback) { + callback(new Error(), {}); + }; + return should(elasticsearch.deleteByQuery(requestObject)).be.rejected(); }); @@ -346,31 +481,49 @@ describe('Test: ElasticSearch service', function () { // import (bulk) it('should support bulk data import', function () { requestObject.data.body = [ - { index: {_id: 1, _type: collection } }, + { index: {_id: 1, _type: collection, _index: index } }, { firstName: 'foo' }, - { index: {_id: 2, _type: collection } }, + { index: {_id: 2, _type: collection, _index: index } }, { firstName: 'bar' }, - { update: {_id: 1, _type: collection } }, + { update: {_id: 1, _type: collection, _index: index } }, { doc: { firstName: 'foobar' } }, - { delete: {_id: 2, _type: collection } } + { delete: {_id: 2, _type: collection, _index: index } } ]; + elasticsearch.client.bulk = function (data) { + should(data.body).be.exactly(requestObject.data.body); + + return Promise.resolve({}); + }; + return should(elasticsearch.import(requestObject)).be.fulfilled(); }); it('should raise a "Partial Error" response for bulk data import with some errors', function (done) { requestObject.data.body = [ - { index: {_id: 1, _type: collection } }, + { index: {_id: 1, _type: collection, _index: index } }, { firstName: 'foo' }, - { index: {_id: 2, _type: collection } }, + { index: {_id: 2, _type: collection, _index: index } }, { firstName: 'bar' }, - { update: {_id: 12, _type: collection } }, + { update: {_id: 12, _type: collection, _index: index } }, { doc: { firstName: 'foobar' } }, - { update: {_id: 212, _type: collection } }, + { update: {_id: 212, _type: collection, _index: index } }, { doc: { firstName: 'foobar' } } ]; + elasticsearch.client.bulk = function (data) { + should(data.body).be.exactly(requestObject.data.body); + + return Promise.resolve({ + errors: true, + items: { + 12: {index: {status: 404, error: 'DocumentMissingException'}}, + 212: {index: {status: 404, error: 'DocumentMissingException'}} + } + }); + }; + elasticsearch.import(requestObject) .then(function(result) { try { @@ -389,20 +542,59 @@ describe('Test: ElasticSearch service', function () { }); }); - it('should override the type with the collection if one has been specified in the document', function () { + it('should override the type with the collection if one has been specified in the request', function () { + kuzzle.indexes = {}; + requestObject.data.body = [ - { index: {_id: 1} }, + { index: {_id: 1, _index: index} }, { firstName: 'foo' }, - { index: {_id: 2} }, + { index: {_id: 2, _index: 'indexAlt'} }, { firstName: 'bar' }, - { update: {_id: 1} }, + { update: {_id: 1, _index: index} }, { doc: { firstName: 'foobar' } }, - { delete: {_id: 2} } + { delete: {_id: 2, _index: 'indexAlt'} } ]; + elasticsearch.client.bulk = function (data) { + should(data.body).be.an.Array().and.match([ + { index: {_id: 1, _index: index, _type: collection} }, + { firstName: 'foo' }, + { index: {_id: 2, _index: 'indexAlt', _type: collection} }, + { firstName: 'bar' }, + { update: {_id: 1, _index: index, _type: collection} }, + { doc: { firstName: 'foobar' } }, + { delete: {_id: 2, _index: 'indexAlt', _type: collection} } + ]); + + return Promise.resolve({items: [ + { index: {_id: 1, _index: index, _type: collection} }, + { index: {_id: 2, _index: 'indexAlt', _type: collection} }, + { update: {_id: 1, _index: index, _type: collection} }, + { delete: {_id: 2, _index: 'indexAlt', _type: collection} } + ]}); + }; + return should(elasticsearch.import(requestObject)).be.fulfilled(); }); + it('should reject the import promise if elasticsearch throws an error', function () { + requestObject.data.body = [ + { index: {_id: 1, _index: index} }, + { firstName: 'foo' }, + { index: {_id: 2, _index: index} }, + { firstName: 'bar' }, + { update: {_id: 1, _index: index} }, + { doc: { firstName: 'foobar' } }, + { delete: {_id: 2, _index: index} } + ]; + + elasticsearch.client.bulk = function (data) { + return Promise.reject(new Error()); + }; + + return should(elasticsearch.import(requestObject)).be.rejected(); + }); + it('should return a rejected promise if no body is provided', function () { delete requestObject.data.body; return should(elasticsearch.import(requestObject)).be.rejected(); @@ -410,16 +602,41 @@ describe('Test: ElasticSearch service', function () { it('should return a rejected promise if no type has been provided, locally or globally', function () { delete requestObject.collection; + + requestObject.data.body = [ + { index: {_id: 1, _type: collection, _index: index } }, + { firstName: 'foo' }, + { index: {_id: 2, _type: collection, _index: index } }, + { firstName: 'bar' }, + { update: {_id: 1, _index: index} }, + { doc: { firstName: 'foobar' } }, + { delete: {_id: 2, _type: collection, _index: index } } + ]; + + elasticsearch.client.bulk = function (data) { + return Promise.resolve({}); + }; + + return should(elasticsearch.import(requestObject)).be.rejected(); + }); + + it('should return a rejected promise if no index has been provided, locally or globally', function () { + delete requestObject.index; + requestObject.data.body = [ - { index: {_id: 1, _type: collection } }, + { index: {_id: 1, _type: collection, _index: index } }, { firstName: 'foo' }, - { index: {_id: 2, _type: collection } }, + { index: {_id: 2, _type: collection, _index: index } }, { firstName: 'bar' }, - { update: {_id: 1} }, + { update: {_id: 1, _type: collection} }, { doc: { firstName: 'foobar' } }, - { delete: {_id: 2, _type: collection } } + { delete: {_id: 2, _type: collection, _index: index } } ]; + elasticsearch.client.bulk = function (data) { + return Promise.resolve({}); + }; + return should(elasticsearch.import(requestObject)).be.rejected(); }); @@ -430,105 +647,96 @@ describe('Test: ElasticSearch service', function () { city: {type: 'string'} } }; + elasticsearch.client.indices.putMapping = function (data) { + should(data.body).be.exactly(requestObject.data.body); + + return Promise.resolve({}); + }; return should(elasticsearch.putMapping(requestObject)).be.fulfilled(); }); it('should reject bad mapping input', function () { + + elasticsearch.client.indices.putMapping = function (data) { + should(data.body).not.have.key('properties'); + + return Promise.reject({}); + }; + return should(elasticsearch.putMapping(requestObject)).be.rejected(); }); // getMapping it('should allow users to retrieve a mapping', function (done) { + + elasticsearch.client.indices.getMapping = function (data) { + var mappings = {}; + + mappings[index] = {mappings: {}}; + + return Promise.resolve(mappings); + }; + elasticsearch.getMapping(requestObject) .then(function (result) { should(result.data).not.be.undefined(); - should(result.data.mainindex).not.be.undefined(); - should(result.data.mainindex.mappings).not.be.undefined(); + should(result.data[requestObject.index]).not.be.undefined(); + should(result.data[requestObject.index].mappings).not.be.undefined(); done(); }) - .catch(function (error) { - done(error); - }); + .catch(error => done(error)); }); it('should return a rejected promise if there is no mapping found', function () { requestObject.collection = 'foobar'; + requestObject.index = 'kuzzle-unit-tests-fakeindex'; + + elasticsearch.client.indices.getMapping = function (data) { + var mappings = {}; + + mappings[index] = {mappings: {}}; + mappings[index].mappings[collection] = {}; + + return Promise.resolve(mappings); + }; + return should(elasticsearch.getMapping(requestObject)).be.rejected(); }); it('should reject the getMapping promise if elasticsearch throws an error', function () { - kuzzle.config[engineType].index = 'kuzzle-unit-tests-fakeindex'; - delete requestObject.data.body; - return should(elasticsearch.getMapping(requestObject)).be.rejected(); - }); - // deleteCollection - it('should allow deleting an entire collection', function () { - delete requestObject.data.body; - return should(elasticsearch.deleteCollection(requestObject)).be.fulfilled(); - }); + elasticsearch.client.indices.getMapping = function (data) { + return Promise.reject(new Error()); + }; - it('should return a rejected promise if the delete collection function fails', function () { - // because we already deleted the collection in the previous test, it should naturally fail - delete requestObject.data.body; - return should(elasticsearch.deleteCollection(requestObject)).be.rejected(); + return should(elasticsearch.getMapping(requestObject)).be.rejected(); }); - // reset - it('should allow resetting the database', function (done) { - var - ret, - deletedAll = false, - mainindexCreated = false; - - elasticsearch.client.indices.delete = function (param) { - try { - should(param).be.an.Object().and.match({index: '_all'}); - deletedAll = true; - return Promise.resolve({}); - } - catch (error) { - done(error); - } - }; + // deleteCollection + it('should allow deleting an entire collection', function (done) { - elasticsearch.client.indices.create = function (param) { - try { - should(param).be.an.Object().and.match({index: 'mainindex'}); - mainindexCreated = true; - return Promise.resolve({}); - } - catch (error) { - done(error); - } + elasticsearch.client.indices.deleteMapping = function (data) { + return Promise.resolve({}); }; - ret = elasticsearch.reset(); - should(ret).be.a.Promise(); - - ret - .then(function () { - should(deletedAll).be.true(); - should(mainindexCreated).be.true(); + delete requestObject.data.body; + should(elasticsearch.deleteCollection(requestObject)).be.fulfilled() + .then(function (result) { + should(kuzzle.indexes).be.an.instanceOf(Object).and.have.property(index, []); done(); }) - .catch(function (error) { - done(error); - }); - }); - - it('should return a rejected promise if the reset fails while creating the main index', function () { - elasticsearch.client.indices.delete = function () { return Promise.resolve({}); }; - elasticsearch.client.indices.create = function () { return Promise.reject(new Error('rejected')); }; - - return should(elasticsearch.reset()).be.rejected(); + .catch(error => done(error)); }); - it('should return a rejected promise if the reset fails while deleting the database', function () { - elasticsearch.client.indices.delete = function () { return Promise.reject(new Error('rejected')); }; + it('should return a rejected promise if the delete collection function fails', function () { + // because we already deleted the collection in the previous test, it should naturally fail + elasticsearch.client.indices.deleteMapping = function (data) { + return Promise.reject(new Error()); + }; - return should(elasticsearch.reset()).be.rejected(); + delete requestObject.data.body; + return should(elasticsearch.deleteCollection(requestObject)).be.rejected(); }); // getAllIdsFromQuery @@ -560,9 +768,7 @@ describe('Test: ElasticSearch service', function () { should(result.length).be.exactly(2); done(); }) - .catch(function (error) { - done(error); - }); + .catch(error => done(error)); }); it('should return a rejected promise if the search fails', function () { @@ -611,35 +817,264 @@ describe('Test: ElasticSearch service', function () { should(result.length).be.exactly(2); done(); }) - .catch(function (error) { - done(error); - }); + .catch(error => done(error)); }); // listCollections it('should allow listing all available collections', function () { + + elasticsearch.client.indices.getMapping = function (data) { + var mappings = {}; + + mappings[index] = {mappings: {}}; + mappings[index].mappings[collection] = {}; + + return Promise.resolve(mappings); + }; + delete requestObject.data.body; return should(elasticsearch.listCollections(requestObject)).be.fulfilled(); }); it('should reject the listCollections promise if elasticsearch throws an error', function () { - kuzzle.config[engineType].index = 'kuzzle-unit-tests-fakeindex'; + + elasticsearch.client.indices.getMapping = function (data) { + return Promise.reject(new Error()); + }; + + requestObject.index = 'kuzzle-unit-tests-fakeindex'; delete requestObject.data.body; return should(elasticsearch.listCollections(requestObject)).be.rejected(); }); // createCollection - it('should allow creating a new collection', function () { - return should(elasticsearch.createCollection(requestObject)).be.fulfilled(); + it('should allow creating a new collection', function (done) { + + elasticsearch.client.indices.putMapping = function (data) { + return Promise.resolve(); + }; + + requestObject.collection = '%foobar'; + elasticsearch.createCollection(requestObject) + .then(function (result) { + should(kuzzle.indexes).be.an.instanceOf(Object).and.have.property(index, [collection, requestObject.collection]); + done(); + }) + .catch(error => done(error)); + }); + + it('should reject the createCollection promise if elasticsearch throws an error', function () { + + elasticsearch.client.indices.putMapping = function (data) { + return Promise.reject(new Error()); + }; + + return should(elasticsearch.createCollection(requestObject)).be.rejected(); }); // truncateCollection - it('should allow truncating an existing collection', function () { - return should(elasticsearch.truncateCollection(requestObject)).be.fulfilled(); + it('should allow truncating an existing collection', function (done) { + var + mapping = {}, + hasRetrievedMapping = false, + hasDeletedMapping = false, + hasCreatedMapping = false; + + mapping[index] = {mappings: {}}; + mapping[index].mappings[collection] = {foo: 'bar'}; + + elasticsearch.client.indices.getMapping = function (data) { + hasRetrievedMapping = true; + return Promise.resolve(mapping); + }; + elasticsearch.client.indices.deleteMapping = function (data) { + hasDeletedMapping = true; + return Promise.resolve(); + }; + elasticsearch.client.indices.putMapping = function (data) { + should(data.body[data.type]).be.exactly(mapping[requestObject.index].mappings[requestObject.collection]); + hasCreatedMapping = true; + return Promise.resolve(); + }; + + elasticsearch.truncateCollection(requestObject) + .then(function (result) { + should(hasRetrievedMapping).be.exactly(true); + should(hasDeletedMapping).be.exactly(true); + should(hasCreatedMapping).be.exactly(true); + done(); + }) + .catch(error => done(error)); }); it('should return an error if trying to truncate a non-existing collection', function () { + var + mapping = {}; + + mapping[index] = {mappings: {}}; + mapping[index].mappings[collection] = {foo: 'bar'}; + + elasticsearch.client.indices.getMapping = function (data) { + return Promise.resolve(mapping); + }; + elasticsearch.client.indices.deleteMapping = function (data) { + return Promise.reject(); + }; + requestObject.collection = 'non existing collection'; return should(elasticsearch.truncateCollection(requestObject)).be.rejected(); }); + + it('should return an error if trying to truncate a non-existing collection into an non-existing index', function () { + var + mapping = {}; + + mapping[index] = {mappings: {}}; + mapping[index].mappings[collection] = {foo: 'bar'}; + + elasticsearch.client.indices.getMapping = function (data) { + return Promise.resolve(mapping); + }; + + requestObject.index = 'non existing index'; + return should(elasticsearch.truncateCollection(requestObject)).be.rejected(); + }); + + // reset + it('should allow deleting all indexes', function (done) { + var + ret, + deletedAll = false; + + elasticsearch.client.cat.indices = function (data) { + return Promise.resolve(' \n %kuzzle \n ' + index + ' \n '); + }; + + elasticsearch.client.indices.delete = function (param) { + try { + should(param).be.an.Object().and.match({index: [index]}); + deletedAll = true; + return Promise.resolve({}); + } + catch (error) { + done(error); + return Promise.reject(error); + } + }; + + ret = elasticsearch.deleteIndexes(requestObject); + should(ret).be.a.Promise(); + + ret + .then(function () { + should(kuzzle.indexes).be.an.instanceOf(Object).and.have.keys(); + should(deletedAll).be.true(); + done(); + }) + .catch(error => done(error)); + }); + + it('should return a rejected promise if the reset fails while deleting all indexes', function () { + elasticsearch.client.indices.getMapping = function (data) { + var indexes = {}; + indexes['%kuzzle'] = []; + indexes[index] = []; + return Promise.resolve(indexes); + }; + elasticsearch.client.indices.delete = function () { return Promise.reject(new Error('rejected')); }; + + return should(elasticsearch.deleteIndexes(requestObject)).be.rejected(); + }); + + it('should not delete any index if only left %kuzzle internal index', function () { + elasticsearch.client.indices.getMapping = function (data) { + var indexes = {}; + indexes['%kuzzle'] = []; + return Promise.resolve(indexes); + }; + elasticsearch.client.indices.delete = function () { return Promise.reject(new Error('rejected')); }; + + return should(elasticsearch.deleteIndexes(requestObject)).be.fulfilled(); + }); + + it('should be able to create index', function (done) { + var ret; + + kuzzle.indexes = {}; + + elasticsearch.client.indices.create = function(data) { + should(data.index).be.exactly(requestObject.index); + + return Promise.resolve({}); + }; + + ret = elasticsearch.createIndex(requestObject); + should(ret).be.a.Promise(); + + ret + .then(function(result) { + should(kuzzle.indexes).be.an.instanceOf(Object).and.have.property(index, []); + done(); + }) + .catch(error => done(error)); + }); + + it('should reject the createIndex promise if elasticsearch throws an error', function () { + elasticsearch.client.indices.create = function(data) { + return Promise.reject(new Error()); + }; + + return should(elasticsearch.createIndex(requestObject)).be.rejected(); + }); + + it('should be able to delete index', function (done) { + var ret; + + elasticsearch.client.indices.delete = function(data) { + should(data.index).be.exactly(requestObject.index); + + return Promise.resolve({}); + }; + + ret = elasticsearch.deleteIndex(requestObject); + should(ret).be.a.Promise(); + + ret + .then(function(result) { + should(kuzzle.indexes).be.an.instanceOf(Object).and.have.keys(); + done(); + }) + .catch(error => done(error)); + }); + + it('should reject the deleteIndex promise if elasticsearch throws an error', function () { + elasticsearch.client.indices.delete = function(data) { + return Promise.reject(new Error()); + }; + + return should(elasticsearch.deleteIndex(requestObject)).be.rejected(); + }); + + it('should allow listing indexes', function (done) { + elasticsearch.client.indices.getMapping = function (data) { + var indexes = {}; + indexes[index] = []; + return Promise.resolve(indexes); + }; + + elasticsearch.listIndexes(requestObject) + .then(result => { + should(result.data.indexes).be.an.instanceOf(Array).and.match([index]); + done(); + }) + .catch(error => done(error)); + }); + + it('should reject the listIndexes promise if elasticsearch throws an error', function () { + elasticsearch.client.indices.getMapping = function (data) { + return Promise.reject(new Error()); + }; + + return should(elasticsearch.listIndexes(requestObject)).be.rejected(); + }); });