diff --git a/.kuzzlerc b/.kuzzlerc index bbb7064384..3a839058eb 100644 --- a/.kuzzlerc +++ b/.kuzzlerc @@ -1,6 +1,6 @@ { // Kuzzle server listening port - "port": 7512, + "port": 7511, // Host and port for ipBroker service that handle internal communication "ipcBroker": { "host": "localhost", diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 35f9f43a4b..00e0660f5b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,8 @@ docker-compose -f docker-compose/debug.yml up You can now access to: -* http://localhost:7512/api for standard Kuzzle API +* http://localhost:7511/api for the standard Kuzzle REST API +* ws://localhost:7512 for the Socket.io API (needs the [Socket.io](https://github.com/kuzzleio/kuzzle-plugin-socketio) plugin installed) * http://127.0.0.1:8080/debug?port=7000 to debug the server * http://127.0.0.1:8081/debug?port=7001 to debug the worker diff --git a/app-start.js b/app-start.js index e191ddd74f..cf2ebcfbd9 100644 --- a/app-start.js +++ b/app-start.js @@ -35,14 +35,32 @@ if (process.env.FEATURE_COVERAGE == 1) { rc = require('rc'); kuzzle.start(rc('kuzzle')) - .then(function () { - return kuzzle.cleanDb(); - }) - .then(function () { - return kuzzle.prepareDb(); + .then(() => { return kuzzle.cleanDb(); }) + .then(() => { return kuzzle.prepareDb(); }) + .then(() => { + console.log( + ` + ▄▄▄▄▄ ▄███▄ ▄▄▄▄ + ▄█████████▄▄█████████▄▄████████▄ + ██████████████████████████████████ + ▀██████████████████████████████▀ + ▄███████████████████████████▄ + ▄███████████████████████████████▄ + ▀█████████████████████████████████▀ + ▀██▀ ▀██████▀ ▀██▀ + ██ ████ ██ + ▄████▄ + ▀████▀ + ▀▀` + ); + + console.log(` +████████████████████████████████████ +██ KUZZLE ` + (kuzzle.isServer ? 'SERVER' : 'WORKER') + ` STARTED ██ +████████████████████████████████████`); }) - .catch(function (error) { - kuzzle.log.error(error); + .catch(error => { + console.error(error); + process.exit(1); }); - })(); diff --git a/bin/kuzzle-start.js b/bin/kuzzle-start.js index 921dadd90f..1bfe5fe6b4 100644 --- a/bin/kuzzle-start.js +++ b/bin/kuzzle-start.js @@ -1,22 +1,39 @@ #!/usr/bin/env node var rc = require('rc'), - winston = require('winston'), kuzzle = require('../lib'); module.exports = function () { - var log = winston; - log.info('Starting Kuzzle'); + console.log('Starting Kuzzle'); kuzzle.start(rc('kuzzle')) - .then(function () { - return kuzzle.cleanDb(); - }) - .then(function () { - return kuzzle.prepareDb(); + .then(() => { return kuzzle.cleanDb(); }) + .then(() => { return kuzzle.prepareDb(); }) + .then(() => { + console.log( + ` + ▄▄▄▄▄ ▄███▄ ▄▄▄▄ + ▄█████████▄▄█████████▄▄████████▄ + ██████████████████████████████████ + ▀██████████████████████████████▀ + ▄███████████████████████████▄ + ▄███████████████████████████████▄ + ▀█████████████████████████████████▀ + ▀██▀ ▀██████▀ ▀██▀ + ██ ████ ██ + ▄████▄ + ▀████▀ + ▀▀` + ); + + console.log(` +████████████████████████████████████ +██ KUZZLE ` + (kuzzle.isServer ? 'SERVER' : 'WORKER') + ` STARTED ██ +████████████████████████████████████`); }) - .catch(function (error) { - kuzzle.log.error(error); + .catch(error => { + console.error(error); + process.exit(1); }); }; \ No newline at end of file diff --git a/config/defaultPlugins.json b/config/defaultPlugins.json index dab075973d..9e084c193e 100644 --- a/config/defaultPlugins.json +++ b/config/defaultPlugins.json @@ -1,16 +1,29 @@ { "kuzzle-plugin-logger": { - "version": "1.0.4", + "version": "1.0.6", "activated": true, + "name": "kuzzle-plugin-logger", "defaultConfig": { "service": "winston", "level": "info", - "addDate": true + "addDate": true, + "loadedBy": "all" + } + }, + "kuzzle-plugin-socketio": { + "version": "1.0.3", + "activated": true, + "name": "kuzzle-plugin-socketio", + "defaultConfig": { + "port": 7512, + "room": "kuzzle", + "loadedBy": "server" } }, "kuzzle-plugin-auth-passport-local": { "url": "git+https://github.com/kuzzleio/kuzzle-plugin-auth-passport-local.git", "activated": true, + "name": "kuzzle-plugin-auth-passport-local", "defaultConfig": {} } } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index d62fb474e9..ccd997c8f2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,7 @@ kuzzle: image: kuzzleio/kuzzle ports: + - "7511:7511" - "7512:7512" links: - elasticsearch diff --git a/docker-compose/debug.yml b/docker-compose/debug.yml index b87c681329..5ec67dd842 100644 --- a/docker-compose/debug.yml +++ b/docker-compose/debug.yml @@ -4,6 +4,7 @@ kuzzle: volumes: - "..:/var/app" ports: + - "7511:7511" - "7512:7512" - "8080:8080" - "8081:8081" @@ -14,7 +15,7 @@ kuzzle: environment: - MQ_BROKER_ENABLED=1 - LIKE_A_VIRGIN - - FIXTURES + - FIXTURES=/var/app/features/fixtures/functionalTestsFixtures.json - FEATURE_COVERAGE rabbit: diff --git a/docker-compose/perf.yml b/docker-compose/perf.yml index abee0fcfe9..06f3a1604f 100644 --- a/docker-compose/perf.yml +++ b/docker-compose/perf.yml @@ -4,6 +4,7 @@ kuzzle: - "..:/var/app" - "/var/log" ports: + - "7511:7511" - "7512:7512" command: /run-perf.sh links: diff --git a/docker-compose/test-travis.yml b/docker-compose/test-travis.yml index ebf198ba49..0dcda99982 100644 --- a/docker-compose/test-travis.yml +++ b/docker-compose/test-travis.yml @@ -4,6 +4,7 @@ kuzzle: volumes: - "..:/var/app" ports: + - "7511:7511" - "7512:7512" links: - rabbit @@ -11,7 +12,7 @@ kuzzle: - redis environment: - LIKE_A_VIRGIN=1 - - FIXTURES + - FIXTURES=/var/app/features/fixtures/functionalTestsFixtures.json - MQ_BROKER_ENABLED=1 - FEATURE_COVERAGE=1 # Travis env var must be propagated into the container diff --git a/docker-compose/test.yml b/docker-compose/test.yml index 8d74142a98..11f7cae6ec 100644 --- a/docker-compose/test.yml +++ b/docker-compose/test.yml @@ -4,6 +4,7 @@ kuzzle: volumes: - "..:/var/app" ports: + - "7511:7511" - "7512:7512" links: - rabbit @@ -11,7 +12,7 @@ kuzzle: - redis environment: - LIKE_A_VIRGIN=1 - - FIXTURES + - FIXTURES=/var/app/features/fixtures/functionalTestsFixtures.json - MQ_BROKER_ENABLED=1 - FEATURE_COVERAGE=1 diff --git a/docs/MQ-broker.md b/docs/MQ-broker.md index b585e3951a..13c0d657c8 100644 --- a/docs/MQ-broker.md +++ b/docs/MQ-broker.md @@ -22,6 +22,7 @@ And add an environment variable and link in the main Kuzzle container: kuzzle: image: kuzzleio/kuzzle ports: + - "7511:7511" - "7512:7512" links: - rabbit diff --git a/docs/installation.md b/docs/installation.md index 82af31584e..b80153244d 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,5 +1,17 @@ # Installation +## Table of Contents + + * [Using the all-in-one Docker recipe](#using-the-all-in-one-docker-recipe) + * [Reset Kuzzle with Docker recipe](#reset-kuzzle-with-docker-recipe) + * [Reset Kuzzle and insert sorme fixtures with Docker recipe](#reset-kuzzle-and-insert-sorme-fixtures-with-docker-recipe) + * [Initialize Kuzzle mapping with Docker recipe](#initialize-kuzzle-mapping-with-docker-recipe) + * [Using Vagrant](#using-vagrant) + * [From source or NPM](#from-source-or-npm) + * [Manual install](#manual-install) + * [Default](#default) + * [Change external services hosts](#change-external-services-hosts) + ## Using the all-in-one Docker recipe If you are running Docker and just want to get your own Kuzzle running, you can use the provided docker-compose file. diff --git a/docs/plugins.md b/docs/plugins.md index 835cc83cc7..8d674a8b5c 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -1,3 +1,30 @@ +# Table of Contents + +* [Table of Contents](#table-of-contents) +* [About](#about) +* [Plugins Configuration](#plugins-configuration) +* [Default plugins](#default-plugins) + * [Logger](#logger) + * ["Passport Local" Authentication](#passport-local-authentication" aria-hidden="true">"Passport Local) + * [Socket.io communication support](#socketio-communication-support) +* [How to create a plugin](#how-to-create-a-plugin) + * [Configuration](#configuration) + * [The plugin context](#the-plugin-context) + * [Architecture](#architecture) + * [The plugin init function](#the-plugin-init-function) + * [Listener plugins](#listener-plugins) + * [Pipe plugins](#pipe-plugins) + * [Controllers](#controllers) + * [How it works](#how-it-works) + * [Protocol plugins](#protocol-plugins) + * [How it works](#how-it-works-1) + * [Example](#example) + * [Examples](#examples) +* [Troubleshooting](#troubleshooting) + * [Proxy](#proxy) + + + # About Plugins are external components allowing to execute functions on specific event triggering. @@ -7,32 +34,35 @@ There are several types of plugins: * Pipe events: perform an action and return something. Kuzzle is waiting that all pipe events are performed before continuing. * Controllers: add a specific controller to Kuzzle. -# Configuration +# Plugins Configuration -To customize plugins, you can create a file `config/customPlugins.json`. This file can override default plugin files `config/customPlugins.json` to add/remove plugins and their configurations. -If you're using Docker, you can create your own `customPlugins.json` file and mount it in `/var/app/config/customPlugins.json`. In `docker-compose.yml` file, you can have something like +Some plugins can be configured. To customize these plugins, all you have to do is to create a file `config/customPlugins.json`, and to put it in the `config/` Kuzzle directory. -``` +If your Kuzzle is running inside a docker image, you'll have to inject this file in the image. +In `docker-compose.yml` file, you can have something like: + +```yaml kuzzle: image: kuzzleio/kuzzle volumes: - "host/path/to/customPlugins.json:/var/app/config/customPlugins.json" ports: + - "7511:7511" - "7512:7512" links: - elasticsearch - redis ``` -A plugin configuration can have attributes: +Plugins configuration have the following default attributes: * `url`: a git URL where the plugin can be found and cloned. -* `version`: a version corresponding to the version given in the file `package.json` in the plugin module. +* `version`: the NPM package version to download * `customConfig`: config for the plugin. Each plugin has a different configuration (required or optional), check the corresponding plugin documentation for more information. * `defaultConfig`: Don't edit this attribute. The defaultConfig is provided by the plugin itself. If you need to change the configuration, edit the `customConfig` attribute **Note:** -* URL or version are required. The URL is checked first, so if you have set a version and an URL, the version will be ignored. +* A `url` or `version` parameter is required. The URL is checked first, so if you have set both a version and an URL, the version will be ignored. # Default plugins @@ -46,6 +76,12 @@ By default, the a standard "passport-local" plugin is enabled to authenticate us See also the [global authentication mechanism documentation](security/authentication.md). +## Socket.io communication support + +By default, the protocol plugin [socket.io](https://github.com/kuzzleio/kuzzle-plugin-socketio) is installed, allowing to access Kuzzle using Socket.io clients. + +The default plugin configuration opens the port `7512`. This can be changed by injecting a custom plugin configuration file. + # How to create a plugin A plugin is a Javascript module that can be installed with NPM or via a public GIT repository. @@ -56,6 +92,7 @@ The module must have a `package.json` file with a `pluginInfo` entry. The option ```json "pluginInfo": { + "loadedBy": "all", "defaultConfig": { "service": "winston", "level": "info", @@ -64,13 +101,46 @@ The module must have a `package.json` file with a `pluginInfo` entry. The option } ``` +The `loadedBy` option tells Kuzzle to install and load the plugin only by corresponding instance types. +The accepted values are: `all`, `server` and `worker`. Default value: `all`. + +## The plugin context + +Plugins don't have access to the Kuzzle instance. Instead, Kuzzle provides a plugin ``context`` to the ``plugin.init()`` function. + +Here is the list of shared objects contained in the provided ``context``: + +| Object | Purpose | +|--------|------------------------------| +| ``RequestObject`` | Constructor for standardized requests sent to Kuzzle | +| ``ResponseObject`` | Constructor for the standardized Kuzzle non-realtime response objects | +| ``RealTimeResponseObject`` | Constructor for the standardized Kuzzle realtime response objects | +| Errors... | Kuzzle error constructors. The complete list can be found in the ``lib/api/core/errors`` directory | +| ``repositories()`` | Getter function to the security roles, profiles and users repositories | +| ``getRouter()`` | Getter function to the Kuzzle protocol communication system | + ## Architecture Your main javascript file in your plugin must have a function `init` and expose a `hooks` and/or a `pipes` and/or a `controllers` object. All functions defined in these files must be exposed as main object. -### Hooks -Hook events are triggered and are not blocking functions. Typically, if we want to log something, we will use hook events. +## The plugin init function + +All plugins must expose a ``init`` function. Its purpose is to initialize the plugins according to its configuration. + +Kuzzle calls these ``init`` function at startup, during initialization. + +Expected arguments: +``function (config, context, isDummy)`` + +Where: +* ``config``: JSON object containing the plugin configuration (the content of the ``defaultConfig`` or the ``customConfig`` configuration) +* ``context``: the plugin context (see above) +* ``isDummy``: boolean. True: asks the plugin to not really start itself, but instead mock its functionalities (useful when testing plugins, kuzzle, or both) + +### Listener plugins + +Hook events are triggered and are non-blocking functions. Listener plugins are configured to be called on these hooks. ```js // Somewhere in Kuzzle @@ -78,16 +148,18 @@ kuzzle.pluginsManager.trigger('event:hookEvent', message); ``` ```js -// In hooks.js file in the plugin +/* + Plugin hooks configuration. + Let's assume that we store this configuration in a "hooks.js" file + */ module.exports = { 'event:hookEvent': 'myFunction' } ``` ```js -// In main plugin index file -module.exports = function () { - +// Plugin implementation +module.exports = function () this.hooks = require('./config/hooks.js'); this.init = function (config, context, isDummy) { // do something @@ -95,22 +167,23 @@ module.exports = function () { this.myFunction = function (message, event) { console.log('Event', event, 'is triggered'); - console.log('There is the message', message); + console.log('Here is the message', message); } } ``` -### Pipes +### Pipe plugins -When an event pipe is triggered, we are waiting for all the functions attached on this event. A function attached on a pipe event has access to the data and can even change them. -A function must take in its last parameter a callback. This callback must be called at the end of the function with `callback(error, object)`: +When a pipe event is triggered, we are waiting for all plugins attached on this event. A plugin attached on a pipe event has access to the data and can even change them. +A pipe plugin constructor must take in its last parameter a callback. This callback must be called at the end of the function with `callback(error, object)`: * error: if there is an error during the function, this parameter must be set. If everything is ok, you can call the function with null * object: the object to pass to the next function -Functions are called in chain. When the `callback()` function is called, the next function attached on the event is triggered. +Plugins are called in chain. When the `callback()` function is called, the next function attached on the event is triggered. +If the plugin fails to call the callback before timeout, Kuzzle will raise an error and forward it to the requesting clients. -Pipe events are useful when you want to modify or validate an object with a plugin. +Pipe plugins are useful when you want to modify or validate an object. ```js // Somewhere in Kuzzle @@ -121,7 +194,7 @@ kuzzle.pluginsManager.trigger('event:pipeEvent', requestObject) ``` ```js -// in pipes.js file in the plugin +// Plugin pipes configuration module.exports = { 'event:pipeEvent': 'addCreatedAt' } @@ -147,13 +220,13 @@ In this example, in Kuzzle, the `modifiedRequestObject` has now a `createdAt` at ### Controllers -A plugin controller is a plugin that adds some controller actions into Kuzzle. +A plugin controller is a plugin that adds new controller and actions to Kuzzle. It must expose to Kuzzle: __A `controllers` object listing one or more controllers:__ ```js -// in controllers.js file in the plugin +// Plugin controller configuration module.exports = { 'mycontroller': 'MyController' }; @@ -162,43 +235,39 @@ module.exports = { __A `routes` object listing the HTTP routes for the REST API:__ ```js -// in routes.js file in the plugin +// Plugin REST routes configuration module.exports = [ {verb: 'get', url: '/foo/:name', controller: 'mycontroller', action: 'myAction'}, {verb: 'post', url: '/foo', controller: 'mycontroller', action: 'myAction'}, ]; ``` -_NB: you can describe any routes as you want, according to the actions you need to implement.
+_NB: you can describe any routes you want, according to the actions you need to implement.
For each action, you can declare either a GET action, or a POST action, or both of them._ __The controller code, implementing your actions:__ ```js -// in myController.js file -var q = require('q'); - +// Controller implementation module.exports = function MyController (context) { - this.myAction = function (requestObject) var - deferred = q.defer(), responseBody = {}, response; - // here write the code of your action. + // implement here the result of this controller action + // Sample response object creation with the context variable: response = new context.ResponseObject(requestObject, responseBody); // the function must return a Promise: - deferred.resolve(response); - return deferred.promise; + return Promise.resolve(response); }; }; ``` ```js -// In main plugin index file +// Main plugin file module.exports = function () { this.controllers = require('./config/controllers.js'); @@ -226,12 +295,12 @@ which can be used by the controller actions.
#### How it works -* With Websocket and MQ protocols, _controller_ attribute is prefixed by the plugin name:
+* With non-REST protocols, the _controller_ attribute is prefixed with the plugin name. + Sample: ```js { - requestId: 'xxxxxxxxx', controller: 'myplugin/mycontroller', action: 'myAction', body: { @@ -240,29 +309,134 @@ Sample: } ``` -* With REST protocol, we use the routes configured in _routes.js_, prefixed by "\_plugin/" + the plugin name:
+* With REST protocol, we use the routes configured in _routes.js_. +These routes are automatically prefixed with "\_plugin/" + the plugin name. + Samples: GET action: ``` -GET http://kuzzle:7512/api/_plugin/myplugin/foo/John%20Doe +GET http://kuzzle:7511/api/1.0/_plugin/myplugin/foo/John%20Doe ``` POST action: ``` -POST http://kuzzle:7512/api/_plugin/myplugin/foo +POST http://kuzzle:7511/api/1.0/_plugin/myplugin/foo {"name": "John Doe"} ``` +### Protocol plugins + +Kuzzle core only supports REST communications. All other supported protocols are implemented as protocol plugins. +By default, the Kuzzle official docker image is shipped with the [Socket.io](https://github.com/kuzzleio/kuzzle-plugin-socketio) protocol. + +#### How it works + +Protocol plugins allow Kuzzle to support any existing protocol. These plugins ensure a two-way communication between clients and Kuzzle. + +Messages emanating from Kuzzle are emitted using the following hooks. Protocol plugins are free to ignore some or all of these hooks: + +| Hook | Emitted object | Description | +|------|----------------|-----------------------------| +| ``protocol:joinChannel`` | `{channel, id}`| Tells protocol plugins that the connection `id` subscribed to the channel `channel` | +| ``protocol:leaveChannel`` | `{channel, id}` | Tells protocol plugins that the connection `id` left the channel `channel` | +| ``protocol:broadcast`` | `{channel, payload}` | Asks protocol plugins to emit a data `payload` to clients connected to the channel `channel` | + +*For more information about channels, see our [API Documentation](http://kuzzleio.github.io/kuzzle-api-documentation/#on)* + + + +Requests sent by clients to Kuzzle can be forwarded by protocol plugins using methods exposed in the plugin context. +To access these methods, simply call ``context.getRouter().``: + +| Router method | Arguments | Returns | Description | +|-----------------|--------------|---------|--------------------------| +| ``newConnection`` | ``protocol name`` (string)
``connection ID`` (string) | A promise resolving to a ``context`` object | Declare a new connection to Kuzzle. | +| ``execute`` | ``optional JWT Headers`` (string)
``RequestObject`` (object)
``context`` (obtained with ``newConnection``) | A promise resolving to the corresponding ``ResponseObject`` | Execute a client request. | +| ``removeConnection`` | ``context`` (obtained with ``newConnection``) | | Asks Kuzzle to remove the corresponding connection and all its subscriptions | + +#### Example + +First, link protocol hooks to their corresponding implementation methods: +```js +// Content of a hooks.js file: +module.exports = { + 'protocol:broadcast': 'broadcast', + 'protocol:joinChannel': 'join', + 'protocol:leaveChannel': 'leave' +}; +``` + +Then, implement the corresponding methods: +```js +// Protocol plugin implementation +module.exports = function () { + this.hooks = require('./hooks.js'); + // for instance, maintain client contexts in a global object + this.contexts = {}; + + this.init = function (config, context, isDummy) { + // Protocol initialization. Usually opens a network port to listen to + // incoming messages + + // whenever a client is connected + context.getRouter().newConnection("this protocol name", "connection unique ID") + .then(context => { + this.contexts["connection unique ID"] = context; + }); + + // whenever a client sends a request + context.getRouter().execute(null, requestObject, this.contexts["id"]) + .then(response => { + // forward the response to the client + }) + .catch(error => { + // errors are encapsulated in a ResponseObject. You may simply + // forward it to the client too + }); + + // whenever a client is disconnected + context.getRouter().removeConnection(this.contexts["id"]); + }; + + this.broadcast = function (data) { + /* + Linked to the protocol:broadcast hook, emitted + by Kuzzle when a "data.payload" needs to be broadcasted to the + "data.channel" channel + + The payload is a ResponseObject + */ + }; + + this.join = function (data) { + /* + Linked to the protocol:joinChannel hook, emitted + by Kuzzle when the connection "data.id" joins the + channel "data.channel" + */ + }; + + this.leave = function (data) { + /* + Linked to the protocol:leaveChannel hook, emitted + by Kuzzle when the connection "data.id" leaves the + channel "data.channel" + */ + }; +}; +``` + ## Examples -* [kuzzle-plugin-logger](https://github.com/kuzzleio/kuzzle-plugin-logger). -* [kuzzle-plugin-helloworld](https://github.com/kuzzleio/kuzzle-plugin-helloworld). +* [kuzzle-plugin-logger](https://github.com/kuzzleio/kuzzle-plugin-logger) +* [kuzzle-plugin-helloworld](https://github.com/kuzzleio/kuzzle-plugin-helloworld) +* [kuzzle-plugin-socketio](https://github.com/kuzzleio/kuzzle-plugin-socketio) # Troubleshooting ## Proxy -If you are using Docker and your network is behind a proxy, you need to run this [container](https://hub.docker.com/r/klabs/forgetproxy/) to let Kuzzle container use your proxy to download the plugin +If you are using Docker and your network is behind a proxy, you may need to run this [container](https://hub.docker.com/r/klabs/forgetproxy/). This image lets other docker images accessing to external networks using the server proxy configuration. diff --git a/docs/request_scenarios/read_http.md b/docs/request_scenarios/read_http.md index 0303987a8f..27f854bbb6 100644 --- a/docs/request_scenarios/read_http.md +++ b/docs/request_scenarios/read_http.md @@ -14,7 +14,7 @@ The following diagram shows how request data is exchanged between the client app \#1. The REST client asks for a content using a HTTP GET Request For instance, to retrieve the document '739c26bc-7a09-469a-803d-623c4045b0cb' in the collection 'users': -```GET http://kuzzle:7512/api/users/739c26bc-7a09-469a-803d-623c4045b0cb``` +```GET http://kuzzle:7511/api/users/739c26bc-7a09-469a-803d-623c4045b0cb``` \#2. The HTTP router handles the input request and forward the message to the ```Funnel Controller```. diff --git a/features/fixtures/functionalTestsFixtures.json b/features/fixtures/functionalTestsFixtures.json new file mode 100644 index 0000000000..2702667c8f --- /dev/null +++ b/features/fixtures/functionalTestsFixtures.json @@ -0,0 +1,16 @@ +{ + "index-test": { + "testCollection": [ + { "create": { "_id": "alovelace"} }, + { "username": "alovelace", "name": {"first": "Ada", "last": "Lovelace"} } + ] + }, + "index-test-alt": { + "testCollection": [ + { "create": { "_id": "ghopper"} }, + { "username": "ghopper", "name": {"first": "Grace", "last": "Hopper"} } + ] + } +} + + diff --git a/features/step_definitions/collections.js b/features/step_definitions/collections.js index 2de64bb304..69b3f57ecf 100644 --- a/features/step_definitions/collections.js +++ b/features/step_definitions/collections.js @@ -40,7 +40,7 @@ var apiSteps = function () { return callback(); } - callback('Expected to find the collection <' + collection + '> in this collections list: ' + this.result.collections); + callback('Expected to find the collection <' + collection + '> in this collections list: ' + JSON.stringify(this.result.collections)); }); this.Then(/^I remove the collection and schema(?: from index "([^"]*)")?$/, function (index, callback) { diff --git a/features/step_definitions/serverInfo.js b/features/step_definitions/serverInfo.js index aabd4a3af3..1aac23b9b8 100644 --- a/features/step_definitions/serverInfo.js +++ b/features/step_definitions/serverInfo.js @@ -1,5 +1,5 @@ var apiSteps = function () { - this.When('I get server informations', function (callback) { + this.When(/^I get server informations$/, function (callback) { this.api.getServerInfo() .then(body => { if (body.error) { @@ -16,7 +16,7 @@ var apiSteps = function () { .catch(error => callback(error)); }); - this.Then('I can retrieve the Kuzzle API version', function(callback) { + this.Then(/^I can retrieve the Kuzzle API version$/, function(callback) { if (this.result.serverInfo && this.result.serverInfo.kuzzle && this.result.serverInfo.kuzzle.api) { this.apiVersion = this.result.serverInfo.kuzzle.api; return callback(); diff --git a/features/support/apiREST.js b/features/support/apiREST.js index 7a89b74f28..b1b491e078 100644 --- a/features/support/apiREST.js +++ b/features/support/apiREST.js @@ -1,7 +1,7 @@ var config = require('./config')(), rp = require('request-promise'), - apiVersion; + apiVersion = '1.0'; var ApiREST = function () { this.world = null; @@ -13,14 +13,12 @@ ApiREST.prototype.init = function (world) { ApiREST.prototype.disconnect = function () {}; -ApiREST.prototype.pathApi = function (path) { - var basePath = '/api'; - - if (apiVersion) { - basePath += '/' + apiVersion; - } +ApiREST.prototype.apiPath = function (path) { + return config.url + '/api/1.0/' + path; +}; - return config.url + basePath + '/' + path; +ApiREST.prototype.apiBasePath = function (path) { + return config.url + '/api/' + path; }; ApiREST.prototype.callApi = function (options) { @@ -29,7 +27,7 @@ ApiREST.prototype.callApi = function (options) { ApiREST.prototype.get = function (id, index) { var options = { - url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/' + id), + url: this.apiPath(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/' + id), method: 'GET', json: true }; @@ -39,7 +37,7 @@ ApiREST.prototype.get = function (id, index) { ApiREST.prototype.search = function (filters, index) { var options = { - url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_search'), + url: this.apiPath(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_search'), method: 'POST', json: filters }; @@ -49,7 +47,7 @@ ApiREST.prototype.search = function (filters, index) { ApiREST.prototype.count = function (filters, index) { var options = { - url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_count'), + url: this.apiPath(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_count'), method: 'POST', json: filters }; @@ -59,7 +57,7 @@ ApiREST.prototype.count = function (filters, index) { ApiREST.prototype.create = function (body, index) { var options = { - url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_create'), + url: this.apiPath(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_create'), method: 'POST', json: body }; @@ -69,7 +67,7 @@ ApiREST.prototype.create = function (body, index) { ApiREST.prototype.publish = function (body, index) { var options = { - url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection), + url: this.apiPath(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection), method: 'POST', json: body }; @@ -79,7 +77,7 @@ ApiREST.prototype.publish = function (body, index) { ApiREST.prototype.createOrUpdate = function (body, index) { var options = { - url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/' + body._id), + url: this.apiPath(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/' + body._id), method: 'PUT', json: body }; @@ -89,7 +87,7 @@ ApiREST.prototype.createOrUpdate = function (body, index) { ApiREST.prototype.update = function (id, body, index) { var options = { - url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/' + id + '/_update'), + url: this.apiPath(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/' + id + '/_update'), method: 'PUT', json: body }; @@ -99,7 +97,7 @@ ApiREST.prototype.update = function (id, body, index) { ApiREST.prototype.deleteById = function (id, index) { var options = { - url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/' + id), + url: this.apiPath(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/' + id), method: 'DELETE', json: true }; @@ -109,7 +107,7 @@ ApiREST.prototype.deleteById = function (id, index) { ApiREST.prototype.deleteByQuery = function (filters, index) { var options = { - url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_query'), + url: this.apiPath(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_query'), method: 'DELETE', json: filters }; @@ -119,7 +117,7 @@ ApiREST.prototype.deleteByQuery = function (filters, index) { ApiREST.prototype.deleteCollection = function (index) { var options = { - url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection), + url: this.apiPath(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection), method: 'DELETE', json: true }; @@ -129,7 +127,7 @@ ApiREST.prototype.deleteCollection = function (index) { ApiREST.prototype.bulkImport = function (bulk, index) { var options = { - url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_bulk'), + url: this.apiPath(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_bulk'), method: 'POST', json: bulk }; @@ -139,7 +137,7 @@ ApiREST.prototype.bulkImport = function (bulk, index) { ApiREST.prototype.globalBulkImport = function (bulk) { var options = { - url: this.pathApi('_bulk'), + url: this.apiPath('_bulk'), method: 'POST', json: bulk }; @@ -149,7 +147,7 @@ ApiREST.prototype.globalBulkImport = function (bulk) { ApiREST.prototype.putMapping = function (index) { var options = { - url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_mapping'), + url: this.apiPath(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_mapping'), method: 'PUT', json: this.world.schema }; @@ -159,7 +157,7 @@ ApiREST.prototype.putMapping = function (index) { ApiREST.prototype.getStats = function (dates) { var options = { - url: this.pathApi('_getStats'), + url: this.apiPath('_getStats'), method: 'POST', json: dates }; @@ -169,7 +167,7 @@ ApiREST.prototype.getStats = function (dates) { ApiREST.prototype.getLastStats = function () { var options = { - url: this.pathApi('_getLastStats'), + url: this.apiPath('_getLastStats'), method: 'GET', json: {} }; @@ -179,7 +177,7 @@ ApiREST.prototype.getLastStats = function () { ApiREST.prototype.getAllStats = function () { var options = { - url: this.pathApi('_getAllStats'), + url: this.apiPath('_getAllStats'), method: 'GET', json: {} }; @@ -193,7 +191,7 @@ ApiREST.prototype.listCollections = function (index, type) { index = index || this.world.fakeIndex; options = { - url: this.pathApi(index + '/_listCollections'), + url: this.apiPath(index + '/_listCollections'), method: 'GET', json: true }; @@ -207,7 +205,7 @@ ApiREST.prototype.listCollections = function (index, type) { ApiREST.prototype.now = function () { var options = { - url: this.pathApi('_now'), + url: this.apiPath('_now'), method: 'GET', json: true }; @@ -217,7 +215,7 @@ ApiREST.prototype.now = function () { ApiREST.prototype.truncateCollection = function (index) { var options = { - url: this.pathApi(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_truncate'), + url: this.apiPath(((typeof index !== 'string') ? this.world.fakeIndex : index) + '/' + this.world.fakeCollection + '/_truncate'), method: 'DELETE', json: true }; @@ -227,7 +225,7 @@ ApiREST.prototype.truncateCollection = function (index) { ApiREST.prototype.listIndexes = function () { var options = { - url: this.pathApi('_listIndexes'), + url: this.apiPath('_listIndexes'), method: 'GET', json: true }; @@ -237,7 +235,7 @@ ApiREST.prototype.listIndexes = function () { ApiREST.prototype.deleteIndexes = function () { var options = { - url: this.pathApi('_deleteIndexes'), + url: this.apiPath('_deleteIndexes'), method: 'DELETE', json: true }; @@ -247,7 +245,7 @@ ApiREST.prototype.deleteIndexes = function () { ApiREST.prototype.createIndex = function (index) { var options = { - url: this.pathApi(index), + url: this.apiPath(index), method: 'PUT', json: true }; @@ -257,7 +255,7 @@ ApiREST.prototype.createIndex = function (index) { ApiREST.prototype.deleteIndex = function (index) { var options = { - url: this.pathApi(index), + url: this.apiPath(index), method: 'DELETE', json: true }; @@ -267,7 +265,7 @@ ApiREST.prototype.deleteIndex = function (index) { ApiREST.prototype.getServerInfo = function () { var options = { - url: this.pathApi('_serverInfo'), + url: this.apiBasePath('_serverInfo'), method: 'GET', json: true }; diff --git a/features/support/apiWebsocket.js b/features/support/apiWebsocket.js index f82ffef184..e4c60ad565 100644 --- a/features/support/apiWebsocket.js +++ b/features/support/apiWebsocket.js @@ -120,7 +120,7 @@ var initSocket = function (socketName) { } if (!this.listSockets[socketName]) { - socket = io(config.url, { 'force new connection': true }); + socket = io(config.ws, { 'force new connection': true }); this.listSockets[socketName] = socket; // the default socket is the socket with name 'client1' diff --git a/features/support/config.js b/features/support/config.js index d381588379..274db9343f 100644 --- a/features/support/config.js +++ b/features/support/config.js @@ -8,7 +8,8 @@ var config = require('rc')('kuzzle'); module.exports = function () { var defaultUrls = { - url: 'http://localhost:7512', + url: 'http://localhost:7511', + ws: 'http://localhost:7512', mqttUrl: 'mqtt://localhost:1883', amqpUrl: 'amqp://localhost:5672', stompUrl: 'stomp://localhost:61613' diff --git a/features/support/env.js b/features/support/env.js new file mode 100644 index 0000000000..30fb423dec --- /dev/null +++ b/features/support/env.js @@ -0,0 +1,5 @@ +var configure = function () { + this.setDefaultTimeout(30 * 1000); +}; + +module.exports = configure; diff --git a/features/support/hooks.js b/features/support/hooks.js index 29130d3ab0..53d9db2482 100644 --- a/features/support/hooks.js +++ b/features/support/hooks.js @@ -43,28 +43,6 @@ var myHooks = function () { callback(); }); - - 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(); - }) - .catch(function (error) { - callback(new Error(error)); - }); - }); - this.After(function (scenario, callback) { this.api.deleteCollection() .then(function () { diff --git a/lib/api/Kuzzle.js b/lib/api/Kuzzle.js index 5c56d83d41..4e01658e80 100644 --- a/lib/api/Kuzzle.js +++ b/lib/api/Kuzzle.js @@ -1,5 +1,4 @@ var - winston = require('winston'), EventEmitter = require('eventemitter2').EventEmitter2, // build all hooks defined in config/hooks.js file Hooks = require('../hooks'), @@ -22,8 +21,6 @@ function Kuzzle () { this.isServer = false; } - this.log = winston; - // Add hooks, workers and services this.hooks = new Hooks(this); this.workers = new Workers(this); diff --git a/lib/api/controllers/routerController.js b/lib/api/controllers/routerController.js index a7ccdc66c7..5f8cdd8964 100644 --- a/lib/api/controllers/routerController.js +++ b/lib/api/controllers/routerController.js @@ -1,11 +1,13 @@ var _ = require('lodash'), + q = require('q'), stringify = require('json-stable-stringify'), Router = require('router'), bodyParser = require('body-parser'), finalhandler = require('finalhandler'), RequestObject = require('../core/models/requestObject'), ResponseObject = require('../core/models/responseObject'), + PluginImplementationError = require('../core/errors/pluginImplementationError'), BadRequestError = require('../core/errors/badRequestError'); module.exports = function RouterController (kuzzle) { @@ -13,6 +15,103 @@ module.exports = function RouterController (kuzzle) { this.pluginRouter = null; this.routename = 'kuzzle'; this.kuzzle = kuzzle; + this.connections = {}; + + /** + * Declares a new connection on a given protocol. Called by protocol plugins. + * Returns a context object to be used with router.execute() and router.removeConnection() + * + * @param {string} protocol - protocol name + * @param {string} connectionId - unique connection identifier + * @return {promise} connection context, to be used with other router functions + */ + this.newConnection = function (protocol, connectionId) { + var error; + + if (!connectionId || !protocol || typeof connectionId !== 'string' || typeof protocol !== 'string') { + error = new PluginImplementationError('Rejected new connection declaration: invalid arguments'); + kuzzle.pluginsManager.trigger('log:error', error); + return q.reject(error); + } + + if (!this.connections[connectionId]) { + this.connections[connectionId] = { + connection: {type: protocol, id: connectionId}, + user: null + }; + } + + kuzzle.statistics.newConnection(this.connections[connectionId]); + + return q(this.connections[connectionId]); + }; + + /** + * Called by protocol plugins: forward a received request to Kuzzle. + * Return a promise resolved or rejected with the corresponding ResponseObject + * + * A note about the JWT headers: if this value is falsey, if no "authorization" field is found, if the token is not + * properly formatted or if the token itself is invalid, then the corresponding user will automatically + * be set to "anonymous". + * + * @param {Object} jwtHeaders - Optional JWT headers + * @param {Object} requestObject - the request to execute + * @param {String} context - connection context, obtained using the newConnection() method + * @return {Promise} ResponseObject + */ + this.execute = function (jwtHeaders, requestObject, context) { + var error; + + if (!requestObject) { + error = new PluginImplementationError('Request execution error: no provided request'); + kuzzle.pluginsManager.trigger('log:error', error); + return q.reject(new ResponseObject({}, error)); + } + + if (!context || !context.connection) { + error = new PluginImplementationError('Unable to execute request: ' + requestObject + + '\nReason: invalid context. Use context.getRouter().newConnection() to get a valid context.'); + kuzzle.pluginsManager.trigger('log:error', error); + return q.reject(new ResponseObject({}, error)); + } + + if (!context.connection.id || !this.connections[context.connection.id]) { + error = new PluginImplementationError('Unable to execute request: unknown context. ' + + 'Has context.getRouter().newConnection() been called before executing requests?'); + kuzzle.pluginsManager.trigger('log:error', error); + return q.reject(new ResponseObject({}, error)); + } + + return kuzzle.repositories.user.loadFromToken(getBearerTokenFromHeaders(jwtHeaders)) + .then(user => { + context.user = user; + + return kuzzle.funnel.execute(requestObject, context); + }) + .then(response => { + return response; + }) + .catch(e => { + kuzzle.pluginsManager.trigger('log:error', e); + return q.reject(new ResponseObject({}, e)); + }); + }; + + /** + * Called by protocol plugins: removes a connection from the connection pool. + * @param {object} context - connection context, obtained using the newConnection() method + */ + this.removeConnection = function (context) { + if (context.connection.id && this.connections[context.connection.id]) { + delete this.connections[context.connection.id]; + kuzzle.hotelClerk.removeCustomerFromAllRooms(context.connection); + kuzzle.statistics.dropConnection(context.connection); + } + else { + kuzzle.pluginsManager.trigger('log:error', new PluginImplementationError('Unable to remove connection: ' + + context + '.\nReason: unknown context')); + } + }; /** * Initializes the HTTP routes for the Kuzzle REST API. @@ -124,61 +223,6 @@ module.exports = function RouterController (kuzzle) { this.router(request, response, finalhandler(request, response)); }; - /** - * Handles requests coming from websocket connections - * - * @param {Object} socket client - */ - this.routeWebsocket = function (socket) { - var - routerCtrl = this, - context = { - connection: {type: 'websocket', id: socket.id}, - user: null - }; - - kuzzle.statistics.newConnection(context.connection); - - socket.on(routerCtrl.routename, function (data) { - var - requestObject; - - requestObject = new RequestObject(data, {}, 'websocket'); - - kuzzle.pluginsManager.trigger('log:silly', 'Handle Websocket for controller ' + data.controller); - - // Execute the funnel. Forward any non-empty response to the user. - kuzzle.repositories.user.loadFromToken(getBearerTokenFromHeaders(data.headers)) - .then(function (user) { - context.user = user; - - return kuzzle.funnel.execute(requestObject, context); - }) - .then(function onExecuteSuccess (responseObject) { - kuzzle.io.to(socket.id).emit(requestObject.requestId, responseObject.toJson()); - }) - .catch(function onExecuteError(error) { - var errorObject = new ResponseObject({}, error); - kuzzle.pluginsManager.trigger('log:error', error); - kuzzle.io.to(socket.id).emit(requestObject.requestId, errorObject.toJson()); - }); - }); - - // handles socket disconnections - socket.on('disconnect', function () { - kuzzle.pluginsManager.trigger('websocket:disconnect', 'A client is disconnected'); - kuzzle.hotelClerk.removeCustomerFromAllRooms(context.connection); - kuzzle.statistics.dropConnection(context.connection); - }); - - // handles socket crashes - socket.on('error', function (error) { - kuzzle.pluginsManager.trigger('websocket:error', error); - kuzzle.hotelClerk.removeCustomerFromAllRooms(context.connection); - kuzzle.statistics.dropConnection(context.connection); - }); - }; - /** * Handles requests coming from MQ protocols: AMQP, MQTT & STOMP * @@ -246,7 +290,7 @@ module.exports = function RouterController (kuzzle) { kuzzle.services.list.mqBroker.addExchange('mqtt.' + context.connection.id, errorObject.toJson()); } }); - }); // end listenExchange + }); }; }; @@ -335,7 +379,7 @@ function executeFromRest(params, request, response) { /** * Extract the Bearer token from the given headers - * @param {Array} headers + * @param {Object} headers * @returns {*} */ function getBearerTokenFromHeaders (headers) { diff --git a/lib/api/core/errors/pluginImplementationError.js b/lib/api/core/errors/pluginImplementationError.js new file mode 100644 index 0000000000..1129278dca --- /dev/null +++ b/lib/api/core/errors/pluginImplementationError.js @@ -0,0 +1,11 @@ +var + inherits = require('util').inherits, + KuzzleError = require('./kuzzleError'); + +function PluginImplementationError(message) { + KuzzleError.call(this, message + '\nThis is probably not a Kuzzle error, but a problem with a plugin implementation.', 500); +} +inherits(PluginImplementationError, KuzzleError); +PluginImplementationError.prototype.name = 'PluginImplementationError'; + +module.exports = PluginImplementationError; diff --git a/lib/api/core/hotelClerk.js b/lib/api/core/hotelClerk.js index d3b2ea6301..7ddd8bb532 100644 --- a/lib/api/core/hotelClerk.js +++ b/lib/api/core/hotelClerk.js @@ -668,12 +668,9 @@ function removeRoomForCustomer (connection, roomId) { deferred.resolve(roomId); - // @TODO: should call a "leave channel" method instead - if (connection.type === 'websocket' && this.kuzzle.io.sockets.connected[connection.id]) { - Object.keys(this.rooms[roomId].channels).forEach(channel => { - this.kuzzle.io.sockets.connected[connection.id].leave(channel); - }); - } + Object.keys(this.rooms[roomId].channels).forEach(channel => { + this.kuzzle.pluginsManager.trigger('protocol:leaveChannel', {channel, id: connection.id}); + }); this.rooms[roomId].customers.splice(this.rooms[roomId].customers.indexOf(connection.id), 1); @@ -778,27 +775,23 @@ function subscribeToRoom (roomId, requestObject, context) { deferred = q.defer(); createChannelConfiguration(requestObject) - .then(channel => { - var channelName = roomId + '-' + crypto.createHash('md5').update(JSON.stringify(channel)).digest('hex'); + .then(channel => { + var channelName = roomId + '-' + crypto.createHash('md5').update(JSON.stringify(channel)).digest('hex'); - if (!this.customers[connection.id] || !this.customers[connection.id][roomId]) { - addRoomForCustomer.call(this, connection, roomId, requestObject.metadata); - - this.kuzzle.notifier.notify(roomId, { - error: null, - result: new RealTimeResponseObject(roomId, requestObject, {count: this.rooms[roomId].customers.length}) - }); - } + if (!this.customers[connection.id] || !this.customers[connection.id][roomId]) { + addRoomForCustomer.call(this, connection, roomId, requestObject.metadata); - // @TODO: should call a "join channel" method instead - if (connection.type === 'websocket') { - this.kuzzle.io.sockets.connected[connection.id].join(channelName); - } + this.kuzzle.notifier.notify(roomId, { + error: null, + result: new RealTimeResponseObject(roomId, requestObject, {count: this.rooms[roomId].customers.length}) + }); + } - this.rooms[roomId].channels[channelName] = channel; - deferred.resolve(new RealTimeResponseObject(roomId, requestObject, {channel: channelName})); - }) - .catch(error => deferred.reject(error)); + this.kuzzle.pluginsManager.trigger('protocol:joinChannel', {channel: channelName, id: connection.id}); + this.rooms[roomId].channels[channelName] = channel; + deferred.resolve(new RealTimeResponseObject(roomId, requestObject, {channel: channelName})); + }) + .catch(error => deferred.reject(error)); return deferred.promise; } diff --git a/lib/api/core/notifier.js b/lib/api/core/notifier.js index 3f9d8368fb..a2876cc365 100644 --- a/lib/api/core/notifier.js +++ b/lib/api/core/notifier.js @@ -22,7 +22,6 @@ module.exports = function NotifierController (kuzzle) { * * @param rooms * @param {Object} response - * @param connection * @returns {boolean} */ this.notify = function (rooms, response) { @@ -84,11 +83,7 @@ module.exports = function NotifierController (kuzzle) { */ function send (room, responseObject) { this.hotelClerk.getChannels(room, responseObject).forEach(channel => { - // @TODO: replace with the protocol plugin broadcast functions - if (this.io) { - this.io.to(channel).emit(channel, responseObject); - } - + this.pluginsManager.trigger('protocol:broadcast', {channel, payload: responseObject}); this.services.list.mqBroker.addExchange(channel, responseObject); }); } diff --git a/lib/api/core/pluginsContext.js b/lib/api/core/pluginsContext.js index b565aebe50..7d88269fb3 100644 --- a/lib/api/core/pluginsContext.js +++ b/lib/api/core/pluginsContext.js @@ -5,6 +5,7 @@ var module.exports = function PluginContext(kuzzle) { var context = { + RequestObject: require('./models/requestObject'), ResponseObject: require('./models/responseObject'), RealTimeResponseObject: require('./models/realTimeResponseObject') }, @@ -23,10 +24,18 @@ module.exports = function PluginContext(kuzzle) { walk.walkSync(path.join(__dirname, 'errors'), options); // Add lazy-loading repositories getter: - context.repositories = function() { + context.repositories = function () { return kuzzle.repositories; }; + // Add lazy-loading router getter: + context.getRouter = function () { + return { + newConnection: kuzzle.router.newConnection.bind(kuzzle.router), + execute: kuzzle.router.execute.bind(kuzzle.router), + removeConnection: kuzzle.router.removeConnection.bind(kuzzle.router) + }; + }; return context; }; diff --git a/lib/api/core/pluginsManager.js b/lib/api/core/pluginsManager.js index 65d31600bf..90e68b37dc 100644 --- a/lib/api/core/pluginsManager.js +++ b/lib/api/core/pluginsManager.js @@ -8,60 +8,105 @@ var path = require('path'), _ = require('lodash'), childProcess = require('child_process'), - pathConfig = path.join(__dirname, '..', '..', '..', 'config'); + pathConfig = path.join(__dirname, '..', '..', '..', 'config'), + lockfile = require('proper-lockfile'); -module.exports = function PluginsManager (kuzzle) { +/* + We use the console to display information, as there may be no logger plugin available while installing/launching + plugins + */ + +/*eslint-disable no-console */ + +module.exports = function PluginsManager (kuzzle) { this.kuzzle = kuzzle; this.plugins = {}; this.pipes = {}; this.controllers = {}; this.routes = []; this.isDummy = false; + this.isServer = false; this.config = kuzzle.config.pluginsManager; /** * Initialize configured plugin in config/defaultPlugins.json and config/customPlugins.json * - * @param {Boolean} install, true if modules must be installed and JSON file synchronized + * @param {Boolean} isServer, true if this is a server instance, false for worker instances * @param {Boolean} isDummy, true if we are trying to test pluginsManager + * @returns {Promise} */ - this.init = function (install, isDummy) { + this.init = function (isServer, isDummy) { var pathDefaultPlugins = path.join(pathConfig, 'defaultPlugins.json'), pathCustomPlugins = path.join(pathConfig, 'customPlugins.json'), - defaultPlugins = require(pathDefaultPlugins), - customPlugins = {}; + defaultPlugins, + updateInProgress = false, + customPlugins = {}, + deferred; this.isDummy = isDummy; + this.isServer = isServer; + + if (this.isDummy) { + return q({}); + } if (!childProcess.hasOwnProperty('execSync')) { - kuzzle.log.error('Make sure you\'re using Node version >= 0.12'); + console.error('Make sure you\'re using Node version >= 0.12'); process.exit(1); } - if (fs.existsSync(pathCustomPlugins)) { - customPlugins = require(pathCustomPlugins); - } + deferred = q.defer(); + + /* + Prevents multiple plugin installations at the same time. + Delay installation for an instance only if it's installed on the same computer than another one. + + Skip plugin installation if we're locked, as it means that another instance is already updating plugins + */ + updateInProgress = fs.existsSync('./node_modules.lock'); - if (install) { - if (installPlugins.call(this, defaultPlugins)) { - fs.writeFileSync(pathDefaultPlugins, JSON.stringify(defaultPlugins, null, 2)); + lockfile.lock('./node_modules', {retries: 1000}, (err, release) => { + if (err) { + return deferred.reject(err); } - if (installPlugins.call(this, customPlugins, defaultPlugins)) { - fs.writeFileSync(pathCustomPlugins, JSON.stringify(customPlugins, null, 2)); + defaultPlugins = require(pathDefaultPlugins); + + if (fs.existsSync(pathCustomPlugins)) { + customPlugins = require(pathCustomPlugins); } - } - this.plugins = constructList(defaultPlugins, customPlugins); + if (!updateInProgress) { + if (installPlugins.call(this, defaultPlugins)) { + fs.writeFileSync(pathDefaultPlugins, JSON.stringify(defaultPlugins, null, 2)); + } + + if (installPlugins.call(this, customPlugins, defaultPlugins)) { + fs.writeFileSync(pathCustomPlugins, JSON.stringify(customPlugins, null, 2)); + } + } + + release(); + + this.plugins = constructList(defaultPlugins, customPlugins, this.isServer); + loadPlugins(this.plugins); + deferred.resolve(); + }); + + return deferred.promise; }; /** * Attach events hooks and pipes given by plugins */ this.run = function () { - _.forEach(this.plugins, function (plugin, pluginName) { + if (this.isDummy) { + return false; + } + + _.forEach(this.plugins, (plugin, pluginName) => { var context = new PluginContext(kuzzle), pipeWarnTime = this.config.pipeWarnTime, @@ -71,10 +116,12 @@ module.exports = function PluginsManager (kuzzle) { return true; } + console.log('Starting plugin: ', pluginName); + plugin.object.init(plugin.config, context, this.isDummy); if (plugin.object.hooks) { - _.forEach(plugin.object.hooks, function (fn, event) { + _.forEach(plugin.object.hooks, (fn, event) => { if (plugin.object[fn]) { kuzzle.on(event, function (message) { plugin.object[fn](message, event); @@ -91,7 +138,7 @@ module.exports = function PluginsManager (kuzzle) { pipeTimeout = plugin.config.pipeTimeout; } - _.forEach(plugin.object.pipes, function (fn, pipe) { + _.forEach(plugin.object.pipes, (fn, pipe) => { if (plugin.object[fn]) { if (!this.pipes[pipe]) { this.pipes[pipe] = []; @@ -129,26 +176,27 @@ module.exports = function PluginsManager (kuzzle) { }); }); } - }.bind(this)); + }); } if (plugin.object.controllers) { - _.forEach(plugin.object.controllers, function (controller, controllerName) { + _.forEach(plugin.object.controllers, (controller, controllerName) => { if (plugin.object[controller]) { this.controllers[pluginName+'/'+controllerName] = plugin.object[controller]; } - }.bind(this)); + }); } if (plugin.object.routes) { - async.each(plugin.object.routes, function (route) { + async.each(plugin.object.routes, route => { route.url = '/_plugin/'+pluginName+route.url; route.controller = pluginName+'/'+route.controller; this.routes.push(route); - }.bind(this)); + }); } - }.bind(this)); + console.log('Plugin ' + pluginName + ' started'); + }); }; /** @@ -161,13 +209,13 @@ module.exports = function PluginsManager (kuzzle) { this.trigger = function (event, data) { if (this.isDummy) { this.kuzzle.emit(event, data); - return Promise.resolve(data); + return q(data); } return triggerPipes.call(this, event, data) - .then(function (modifiedData) { + .then(modifiedData => { return triggerHooks.call(this, event, modifiedData); - }.bind(this)); + }); }; /** @@ -189,7 +237,7 @@ module.exports = function PluginsManager (kuzzle) { function triggerHooks(event, data) { this.kuzzle.emit(event, data); - return Promise.resolve(data); + return q(data); } /** @@ -249,7 +297,7 @@ function installPlugins(plugins, basePlugins) { newInstalled = false, pluginInstallId; - _.forEach(plugins, function (plugin, name) { + _.forEach(plugins, (plugin, name) => { if (plugin.url) { pluginInstallId = plugin.url; } @@ -257,7 +305,7 @@ function installPlugins(plugins, basePlugins) { pluginInstallId = name + '@' + plugin.version; } else { - this.kuzzle.log.error('Plugin', name, 'has no version. The version is mandatory if there is no URL.'); + console.error('Plugin', name, 'has no version. The version is mandatory if there is no URL.'); return true; } @@ -265,8 +313,6 @@ function installPlugins(plugins, basePlugins) { return true; } - this.kuzzle.log.info('Install plugin ' + name); - newInstalled = true; npmInstall(pluginInstallId); initConfig.call(this, plugin, name); @@ -276,26 +322,24 @@ function installPlugins(plugins, basePlugins) { if (plugin.activated === undefined) { plugin.activated = (basePlugins !== undefined && basePlugins[name] && basePlugins[name].activated === true); } - - }.bind(this)); + }); return newInstalled; } -function constructList(defaultPlugins, customPlugins, withoutObject) { +function constructList(defaultPlugins, customPlugins, isServer) { var - allPlugins = _.extend(defaultPlugins, customPlugins); + allPlugins = _.extend(defaultPlugins, customPlugins), + pluginsList = {}; + // Load plugins configuration _.forEach(allPlugins, function (plugin, name) { if (!plugin.name) { plugin.name = name; } - if (!withoutObject) { - plugin.object = new (require(name))(); - } - if (!plugin.defaultConfig && !plugin.customConfig) { + plugin.config = {}; return true; } @@ -317,7 +361,22 @@ function constructList(defaultPlugins, customPlugins, withoutObject) { delete plugin.customConfig; }); - return allPlugins; + + /* + Plugins can be loaded by servers or workers. The "loadedBy" property tells by what type of kuzzle instance + the plugin should be loaded. + Accepted values: all, server, worker + Default value: all + */ + _.forEach(allPlugins, (plugin, name) => { + if (plugin.config.loadedBy && plugin.config.loadedBy !== 'all' && (plugin.config.loadedBy === 'server') !== isServer) { + return true; + } + + pluginsList[name] = plugin; + }); + + return pluginsList; } /** @@ -350,7 +409,7 @@ function initConfig(plugin, name) { pluginPackage = require(path.join(getPathPlugin(name), 'package.json')); } catch (e) { - this.kuzzle.log.error(new InternalError('There is a problem with plugin ' + name + '. Check the plugin name')); + console.error(new InternalError('There is a problem with plugin ' + name + '. Check the plugin name')); } // If there is no information about plugin in the package.json @@ -401,6 +460,14 @@ function needInstall(name, from) { * @param name * @returns {String} */ -function getPathPlugin(name) { +function getPathPlugin (name) { return path.join(__dirname, '..', '..', '..', 'node_modules', name); } + +function loadPlugins(plugins) { + _.forEach(plugins, (plugin, name) => { + plugin.object = new (require(name))(); + }); +} + +/*eslint-enable no-console */ diff --git a/lib/api/core/servers.js b/lib/api/core/servers.js index 4028f9679a..f738146636 100644 --- a/lib/api/core/servers.js +++ b/lib/api/core/servers.js @@ -4,10 +4,7 @@ var module.exports = { initAll: function (kuzzle, params) { - var server; - - server = runHttpServer(kuzzle, params); - runWebsocketServer(server, kuzzle, params); + runHttpServer(kuzzle, params); runMQListener(kuzzle, params); } }; @@ -19,7 +16,7 @@ module.exports = { */ function runHttpServer (kuzzle, params) { var - port = process.env.KUZZLE_PORT || params.port || 7512, + port = process.env.KUZZLE_PORT || params.port || 7511, server; kuzzle.router.initRouterHttp(); @@ -34,22 +31,6 @@ function runHttpServer (kuzzle, params) { return server; } -/** - * Run a websocket server forwarding requests to the router controller - * @param server - * @param kuzzle - */ -function runWebsocketServer (server, kuzzle) { - kuzzle.io = require('socket.io')(server); - kuzzle.io.set('origins', '*:*'); - - kuzzle.io.on('connection', function (socket) { - kuzzle.router.routeWebsocket(socket); - }); - - kuzzle.pluginsManager.trigger('server:websocketStarted', 'Starting: WebSocket server'); -} - /** * Asks the router controller to start listening to messages coming from RabbitMQ * @param kuzzle diff --git a/lib/api/prepareDb.js b/lib/api/prepareDb.js index 7ff64fdbfc..2651e20aa3 100644 --- a/lib/api/prepareDb.js +++ b/lib/api/prepareDb.js @@ -116,15 +116,12 @@ function importFixtures() { this.pluginsManager.trigger('log:info', '== Importing fixtures for collection ' + collection + '...'); this.services.list.writeEngine.import(new RequestObject(fixture)) - .then(function () { - callback(); - }) - .catch(function (error) { + .then(() => callback()) + .catch(error => { + this.pluginsManager.trigger('log:error', 'Fixture import error' + error); callback('Fixture import error' + error); }); - }, function (error) { - callbackIndex(error); - }); + }, error => callbackIndex(error)); }, error => { if (error) { deferred.reject(error); diff --git a/lib/api/start.js b/lib/api/start.js index 25c7f11be0..549a226ac6 100644 --- a/lib/api/start.js +++ b/lib/api/start.js @@ -55,67 +55,70 @@ module.exports = function start (params, feature) { this.config.version = packageInfo.version; this.pluginsManager = new PluginsManager(this); - this.pluginsManager.init(this.isServer, feature.dummy); - this.pluginsManager.run(); - - if (!this.isWorker) { - if (!feature.dummy) { - 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; + this.pluginsManager.init(this.isServer, feature.dummy) + .then(() => { + this.pluginsManager.run(); + + if (!this.isWorker) { + if (!feature.dummy) { + 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']}); - } + }); + } + else { + this.services.init({server: false, blacklist: ['mqBroker', 'perf', 'writeEngine', 'readEngine', 'notificationCache', 'monitoring', 'remoteActions', 'statsCache']}); + } - // The funnel controller dispatch messages between the router controller and other controllers - this.funnel = new FunnelController(this); - this.funnel.init(); + // The funnel controller dispatch messages between the router controller and other controllers + this.funnel = new FunnelController(this); + this.funnel.init(); - // The router controller listens to client requests and pass them to the funnel controller - this.router = new RouterController(this); + // The router controller listens to client requests and pass them to the funnel controller + this.router = new RouterController(this); - // Room subscriptions core components - this.hotelClerk = new HotelClerk(this); - this.dsl = new Dsl(this); + // Room subscriptions core components + this.hotelClerk = new HotelClerk(this); + this.dsl = new Dsl(this); - // Notifications core component - this.notifier = new Notifier(this); - this.notifier.init(); + // Notifications core component + this.notifier = new Notifier(this); + this.notifier.init(); - // Statistics core component - this.statistics = new Statistics(this); + // Statistics core component + this.statistics = new Statistics(this); - // Worker response listener - this.workerListener = new WorkerListener(this, this.config.queues.workerWriteResponseQueue); + // Worker response listener + this.workerListener = new WorkerListener(this, this.config.queues.workerWriteResponseQueue); - if (!feature.dummy) { - // Initialize hooks - this.hooks.init(); + if (!feature.dummy) { + // Initialize hooks + this.hooks.init(); - // Starts the servers in charge of listening to client queries (HTTP, MQ or WebSocket) - servers.initAll(this, params); - } - } + // Starts the servers in charge of listening to client queries (HTTP, MQ or WebSocket) + servers.initAll(this, params); + } + } - // Start a single set of workers - if (this.isWorker === undefined || this.isWorker === true) { - this.workers.init(); - } + // Start a single set of workers + if (this.isWorker === undefined || this.isWorker === true) { + this.workers.init(); + } - // the repositories need to be instanciated after the services are initialized - this.repositories = require('./core/models/repositories')(this); + // the repositories need to be instanciated after the services are initialized + this.repositories = require('./core/models/repositories')(this); + kuzzleStarted.resolve({}); + }) + .catch(err => kuzzleStarted.reject(err)); - kuzzleStarted.resolve({}); return kuzzleStarted.promise; }; diff --git a/lib/services/newrelic.js b/lib/services/newrelic.js index 1eb2bc73fb..2da413282d 100644 --- a/lib/services/newrelic.js +++ b/lib/services/newrelic.js @@ -20,7 +20,7 @@ module.exports = function (kuzzle) { this.kuzzle.hooks.add(controller+':websocket:start', 'monitoring:logWs'); }.bind(this)); - this.kuzzle.log('Monitoring service is enabled'); + this.kuzzle.pluginsManager.trigger('log:info', 'Monitoring service is enabled'); }; /** diff --git a/package.json b/package.json index 35017be853..63077b1fef 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "enable": "node bin/kuzzle enable", "disable": "node bin/kuzzle disable", "preversion": "git checkout master && git pull", - "postversion": "git push && git push --tags" + "postversion": "git push && git push --tags" }, "directories": { "lib": "lib" @@ -43,14 +43,13 @@ "node-uuid": "1.4.3", "passport": "0.3.2", "portfinder": "^0.4.0", + "proper-lockfile": "^1.1.1", "q": "2.0.3", "rc": "1.1.2", "redis": "2.3.0", "request-promise": "1.0.2", "router": "1.1.3", - "socket.io": "1.3.7", "walk": "2.3.9", - "winston": "2.0.0", "ws": "0.8.0" }, "repository": { diff --git a/test/api/controllers/adminController.test.js b/test/api/controllers/adminController.test.js index 17fafc540b..bac5d58e5e 100644 --- a/test/api/controllers/adminController.test.js +++ b/test/api/controllers/adminController.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), RequestObject = require.main.require('lib/api/core/models/requestObject'), @@ -15,7 +14,6 @@ describe('Test: admin controller', function () { 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.repositories.role.validateAndSaveRole = role => { diff --git a/test/api/controllers/authController.test.js b/test/api/controllers/authController.test.js index 5058a10b3d..c48bc108c8 100644 --- a/test/api/controllers/authController.test.js +++ b/test/api/controllers/authController.test.js @@ -2,7 +2,6 @@ var should = require('should'), jwt = require('jsonwebtoken'), q = require('q'), - winston = require('winston'), params = require('rc')('kuzzle'), passport = require('passport'), util = require('util'), @@ -64,7 +63,6 @@ describe('Test the auth controller', function () { before(function (done) { requestObject = new RequestObject({ controller: 'auth', action: 'login', body: {strategy: 'mockup', username: 'jdoe'} }, {}, 'unit-test'); kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.start(params, {dummy: true}) .then(function () { passport.use(new MockupStrategy( 'mockup', function(username, callback) { diff --git a/test/api/controllers/bulkController.test.js b/test/api/controllers/bulkController.test.js index 12809b76cd..ed68f1cfef 100644 --- a/test/api/controllers/bulkController.test.js +++ b/test/api/controllers/bulkController.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), RequestObject = require.main.require('lib/api/core/models/requestObject'); @@ -12,13 +11,9 @@ describe('Test the bulk controller', function () { kuzzle, requestObject = new RequestObject({ controller: 'bulk' }, { collection: 'unit-test-bulkController' }, 'unit-test'); - before(function (done) { + before(function () { kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); - kuzzle.start(params, {dummy: true}) - .then(function () { - done(); - }); + return kuzzle.start(params, {dummy: true}); }); it('should activate a hook on a bulk import request', function (done) { diff --git a/test/api/controllers/funnelController/execute.test.js b/test/api/controllers/funnelController/execute.test.js index 1eef6334b6..6b77ed3dfe 100644 --- a/test/api/controllers/funnelController/execute.test.js +++ b/test/api/controllers/funnelController/execute.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), RequestObject = require.main.require('lib/api/core/models/requestObject'), UnauthorizedError = require.main.require('lib/api/core/errors/unauthorizedError'), params = require('rc')('kuzzle'), @@ -22,7 +21,6 @@ describe('Test execute function in funnel controller', function () { beforeEach(function (callback) { kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.removeAllListeners(); kuzzle.start(params, {dummy: true}) .then(function () { diff --git a/test/api/controllers/readController.test.js b/test/api/controllers/readController.test.js index 081984e17d..398749b16c 100644 --- a/test/api/controllers/readController.test.js +++ b/test/api/controllers/readController.test.js @@ -1,7 +1,6 @@ var should = require('should'), q = require('q'), - winston = require('winston'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), BadRequestError = require.main.require('lib/api/core/errors/badRequestError'), @@ -21,7 +20,6 @@ var 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 = { diff --git a/test/api/controllers/routerController/executeFromRest.test.js b/test/api/controllers/routerController/executeFromRest.test.js index e1e06a4907..2a7c139b93 100644 --- a/test/api/controllers/routerController/executeFromRest.test.js +++ b/test/api/controllers/routerController/executeFromRest.test.js @@ -5,7 +5,6 @@ var should = require('should'), - winston = require('winston'), params = require('rc')('kuzzle'), q = require('q'), Kuzzle = require.main.require('lib/api/Kuzzle'), @@ -62,8 +61,6 @@ describe('Test: routerController.executeFromRest', function () { }; kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); - kuzzle.start(params, {dummy: true}) .then(function () { kuzzle.funnel.execute = mockupFunnel; diff --git a/test/api/controllers/routerController/initRouterHttp.test.js b/test/api/controllers/routerController/initRouterHttp.test.js index ddbf1262dd..94d2c72eba 100644 --- a/test/api/controllers/routerController/initRouterHttp.test.js +++ b/test/api/controllers/routerController/initRouterHttp.test.js @@ -5,7 +5,6 @@ var should = require('should'), - winston = require('winston'), http = require('http'), q = require('q'), params = require('rc')('kuzzle'), @@ -68,7 +67,6 @@ describe('Test: routerController.initRouterHttp', function () { */ before(function (done) { kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); var mockResponse = function (params, request, response) { if (!params.action) { diff --git a/test/api/controllers/routerController/routeMQListener.test.js b/test/api/controllers/routerController/routeMQListener.test.js index 6ef1d966a9..9913210f13 100644 --- a/test/api/controllers/routerController/routeMQListener.test.js +++ b/test/api/controllers/routerController/routeMQListener.test.js @@ -5,7 +5,6 @@ var should = require('should'), - winston = require('winston'), q = require('q'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), @@ -54,7 +53,6 @@ describe('Test: routerController.routeMQListener', function () { }; kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.start(params, {dummy: true}) .then(function () { diff --git a/test/api/controllers/routerController/routeWebsocket.test.js b/test/api/controllers/routerController/routeWebsocket.test.js deleted file mode 100644 index 4800676854..0000000000 --- a/test/api/controllers/routerController/routeWebsocket.test.js +++ /dev/null @@ -1,174 +0,0 @@ -/* - * This file tests the routeWebsocket function, which handles websockets - * connections, listens to requests and forward them to the funnel controller. - * - * Since this function only controls events received by the socket passed as - * an argument, we'll use a simple event emitter to test its behavior - */ - -var - should = require('should'), - winston = require('winston'), - params = require('rc')('kuzzle'), - EventEmitter = require('events').EventEmitter, - Kuzzle = require.main.require('lib/api/Kuzzle'), - rewire = require('rewire'), - RouterController = rewire('../../../../lib/api/controllers/routerController'), - RequestObject = require.main.require('lib/api/core/models/requestObject'), - ResponseObject = require.main.require('lib/api/core/models/responseObject'); - - -require('should-promised'); - -describe('Test: routerController.routeWebsocket', function () { - var - kuzzle, - router, - messageSent, - emitter = new EventEmitter(), - forwardedObject = {}, - timer, - timeout = 500; - - before(function (done) { - var - mockupFunnel = function (requestObject) { - forwardedObject = new ResponseObject(requestObject, {}); - - if (requestObject.data.body.resolve) { - if (requestObject.data.body.empty) { - return Promise.resolve({}); - } - else { - return Promise.resolve(forwardedObject); - } - } - else { - return Promise.reject(new Error('rejected')); - } - }; - - kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); - - kuzzle.start(params, {dummy: true}) - .then(function () { - kuzzle.funnel.execute = mockupFunnel; - kuzzle.io = { - emit: () => messageSent = true, - to: () => { return kuzzle.io; } - }; - router = new RouterController(kuzzle); - router.routeWebsocket(emitter); - done(); - }); - }); - - beforeEach(function () { - messageSent = false; - forwardedObject = false; - }); - - it('should have registered a global listener', function () { - should(emitter.listeners(router.routename).length).be.exactly(1); - }); - - it('should embed incoming requests into a well-formed request object', function (done) { - var emittedObject = {body: {resolve: true}, controller: 'read', action: 'get'}; - - emitter.emit(router.routename, emittedObject); - - this.timeout(timeout); - timer = setInterval(function () { - if (forwardedObject === false) { - return; - } - - try { - should(forwardedObject.data.body).not.be.null(); - should(forwardedObject.data.body).be.exactly(emittedObject.body); - should(forwardedObject.protocol).be.exactly('websocket'); - should(forwardedObject.controller).be.exactly('read'); - should(forwardedObject.action).be.exactly('get'); - should(messageSent).be.true(); - done(); - } - catch (e) { - done(e); - } - - clearInterval(timer); - timer = false; - }, 5); - }); - - it('should notify with an error object in case of rejection', function (done) { - var - emittedObject = {body: {resolve: false}, controller: 'read', action: 'get'}, - eventReceived = false; - - kuzzle.once('log:error', () => eventReceived = true); - emitter.emit(router.routename, emittedObject); - - this.timeout(timeout); - timer = setInterval(function () { - if (forwardedObject === false) { - return; - } - - try { - should(messageSent).be.true(); - should(eventReceived).be.true(); - done(); - } - catch (e) { - done(e); - } - - clearInterval(timer); - timer = false; - }, 5); - }); - - it('should handle sockets clean disconnection', function (done) { - var - removeCustomers = false, - hasListener; - - this.timeout(50); - - kuzzle.hotelClerk.removeCustomerFromAllRooms = function () { - removeCustomers = true; - }; - - kuzzle.once('websocket:disconnect', function () { - done(); - }); - - hasListener = emitter.emit('disconnect'); - - should(hasListener).be.true(); - should(removeCustomers).be.true(); - }); - - it('should handle sockets crashes', function (done) { - var - removeCustomers = false, - hasListener; - - this.timeout(50); - - kuzzle.hotelClerk.removeCustomerFromAllRooms = function () { - removeCustomers = true; - }; - - kuzzle.once('websocket:error', function () { - done(); - }); - - hasListener = emitter.emit('error'); - - should(hasListener).be.true(); - should(removeCustomers).be.true(); - }); -}); diff --git a/test/api/controllers/routerController/routerController.test.js b/test/api/controllers/routerController/routerController.test.js index a7b1c208a8..dde9dcc991 100644 --- a/test/api/controllers/routerController/routerController.test.js +++ b/test/api/controllers/routerController/routerController.test.js @@ -1,13 +1,21 @@ var should = require('should'), + params = require('rc')('kuzzle'), + Kuzzle = require.main.require('lib/api/Kuzzle'), rewire = require('rewire'), - RouterController = rewire('../../../../lib/api/controllers/routerController'); + q = require('q'), + RouterController = rewire('../../../../lib/api/controllers/routerController'), + RequestObject = require.main.require('lib/api/core/models/requestObject'), + ResponseObject = require.main.require('lib/api/core/models/responseObject'), + PluginImplementationError = require.main.require('lib/api/core/errors/pluginImplementationError'); -describe('Test: routerController', () => { - var - getBearerTokenFromHeader = RouterController.__get__('getBearerTokenFromHeaders'); +require('should-promised'); +describe('Test: routerController', () => { describe('#getBearerTokenFromHeaders', () => { + var + getBearerTokenFromHeader = RouterController.__get__('getBearerTokenFromHeaders'); + it('should extract the bearer token from the header', () => { var result = getBearerTokenFromHeader({ authorization: 'Bearer sometoken' @@ -16,4 +24,159 @@ describe('Test: routerController', () => { should(result).be.exactly('sometoken'); }); }); + + describe('#newConnection', () => { + var + kuzzle, + protocol = 'foo', + userId = 'bar'; + + before(() => { + kuzzle = new Kuzzle(); + return kuzzle.start(params, {dummy: true}); + }); + + it('should return a promise', () => { + return should(kuzzle.router.newConnection(protocol, userId)).be.fulfilled(); + }); + + it('should have registered the connection', () => { + var context = kuzzle.router.connections[userId]; + should(context).be.an.Object(); + should(context.connection).be.an.Object(); + should(context.connection.id).be.eql(userId); + should(context.connection.type).be.eql(protocol); + should(context.user).be.null(); + }); + + it('should declare a new connection to the statistics core component', () => { + var + newConnectionDeclared = false; + + kuzzle.statistics.newConnection = context => { + should(context).be.an.Object(); + should(context.connection).be.an.Object(); + should(context.connection.id).be.eql(userId); + should(context.connection.type).be.eql(protocol); + should(context.user).be.null(); + newConnectionDeclared = true; + }; + + kuzzle.router.newConnection(protocol, userId); + should(newConnectionDeclared).be.true(); + }); + + it('should return an error if no user ID is provided', () => { + return should(kuzzle.router.newConnection(protocol, undefined)).be.rejectedWith(PluginImplementationError); + }); + + it('should return an error if no protocol is provided', () => { + return should(kuzzle.router.newConnection(undefined, userId)).be.rejectedWith(PluginImplementationError); + }); + }); + + describe('#execute', () => { + var + kuzzle, + context, + requestObject = new RequestObject({ + controller: 'read', + action: 'now' + }); + + before((done) => { + kuzzle = new Kuzzle(); + kuzzle.start(params, {dummy: true}) + .then(() => { + return kuzzle.router.newConnection('foo', 'bar'); + }) + .then(res => { + context = res; + done(); + }) + .catch(error => done(error)); + }); + + it('should return a fulfilled promise with the right arguments', () => { + return should(kuzzle.router.execute(undefined, requestObject, context)).be.fulfilled(); + }); + + it('should return an error if no request object is provided', () => { + return should(kuzzle.router.execute(undefined, undefined, context)).be.rejectedWith(ResponseObject); + }); + + it('should return an error if an invalid context is provided', () => { + return should(kuzzle.router.execute(undefined, requestObject, {})).be.rejectedWith(ResponseObject); + }); + + it('should return an error if an invalid context ID is provided', () => { + var + invalidContext = { + connection: { + id: 'hey', + type: 'jude' + } + }; + return should(kuzzle.router.execute(undefined, requestObject, invalidContext)).be.rejectedWith(ResponseObject); + }); + + it('should forward any error that occured during execution back to the protocol plugin', () => { + kuzzle.funnel.execute = () => { return q.reject(new Error('rejected')); }; + + return should(kuzzle.router.execute(undefined, requestObject, context)).be.rejectedWith(ResponseObject); + }); + }); + + describe('#removeConnection', () => { + var + kuzzle, + context; + + beforeEach((done) => { + kuzzle = new Kuzzle(); + kuzzle.start(params, {dummy: true}) + .then(() => { + return kuzzle.router.newConnection('foo', 'bar'); + }) + .then(res => { + context = res; + done(); + }) + .catch(error => done(error)); + }); + + it('should remove the context from the context pool', () => { + var + unsubscribed = false, + loggedStats = false; + + kuzzle.hotelClerk.removeCustomerFromAllRooms = connection => { + should(connection).be.an.Object().and.be.eql(context.connection); + unsubscribed = true; + }; + + kuzzle.statistics.dropConnection = connection => { + should(connection).be.an.Object().and.be.eql(context.connection); + loggedStats = true; + }; + + kuzzle.router.removeConnection(context); + should(kuzzle.router.connections).be.empty(); + should(unsubscribed).be.true(); + should(loggedStats).be.true(); + }); + + it('should trigger a log:error hook if the context is unknown', function (done) { + var + fakeContext = { + connection: { id: 'Madness? No.', type: 'THIS IS SPARTAAAAAAA!'}, + user: null + }; + + this.timeout(50); + + kuzzle.once('log:error', () => done()); + kuzzle.router.removeConnection(fakeContext); + }); + }); }); diff --git a/test/api/controllers/subscribeController.test.js b/test/api/controllers/subscribeController.test.js index ff08ede5bd..2981c94ea6 100644 --- a/test/api/controllers/subscribeController.test.js +++ b/test/api/controllers/subscribeController.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), RequestObject = require.main.require('lib/api/core/models/requestObject'), @@ -25,7 +24,6 @@ describe('Test: subscribe controller', function () { before(function (done) { context = {}; kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.start(params, {dummy: true}) .then(function () { kuzzle.repositories.role.roles.guest = new Role(); diff --git a/test/api/controllers/writeController.test.js b/test/api/controllers/writeController.test.js index 9a3bf558b7..ddf5ba0764 100644 --- a/test/api/controllers/writeController.test.js +++ b/test/api/controllers/writeController.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), RequestObject = require.main.require('lib/api/core/models/requestObject'); @@ -17,7 +16,6 @@ describe('Test: write controller', function () { 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.writeEngine = {}; diff --git a/test/api/core/hotelClerck/addSubscription.test.js b/test/api/core/hotelClerck/addSubscription.test.js index 4613d4021b..ec517b1af3 100644 --- a/test/api/core/hotelClerck/addSubscription.test.js +++ b/test/api/core/hotelClerck/addSubscription.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), RequestObject = require.main.require('lib/api/core/models/requestObject'), InternalError = require.main.require('lib/api/core/errors/internalError'), BadRequestError = require.main.require('lib/api/core/errors/badRequestError'), @@ -34,7 +33,6 @@ describe('Test: hotelClerk.addSubscription', function () { beforeEach(function (done) { require.cache = {}; kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.removeAllListeners(); return kuzzle.start(params, {dummy: true}) @@ -115,36 +113,24 @@ describe('Test: hotelClerk.addSubscription', function () { }); }); - it('should call a function join when the type is websocket', function () { - var - joinedRooms = [], - requestObject = new RequestObject({ - controller: 'subscribe', - collection: collection, - index: index, - body: filter - }); + it('should trigger a protocol:joinChannel hook', function (done) { + var requestObject = new RequestObject({ + controller: 'subscribe', + collection: collection, + index: index, + body: filter + }); - // mockup internal function kuzzle called when type is websocket - connection.type = 'websocket'; - kuzzle.io = { - sockets: { - connected: { - connectionid: { - join: function (channel) { - joinedRooms.push(channel); - } - } - } - } - }; - kuzzle.notifier = {notify: function () {}}; + this.timeout(50); - return kuzzle.hotelClerk.addSubscription(requestObject, context) - .then(function () { - should(joinedRooms).containEql(channel); - delete connection.type; - }); + kuzzle.once('protocol:joinChannel', (data) => { + should(data).be.an.Object(); + should(data.channel).be.a.String(); + should(data.id).be.eql(context.connection.id); + done(); + }); + + kuzzle.hotelClerk.addSubscription(requestObject, context); }); it('should return the same response when the user has already subscribed to the filter', done => { diff --git a/test/api/core/hotelClerck/countSubscription.test.js b/test/api/core/hotelClerck/countSubscription.test.js index 6bc69386fe..3fee14bb5c 100644 --- a/test/api/core/hotelClerck/countSubscription.test.js +++ b/test/api/core/hotelClerck/countSubscription.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), RequestObject = require.main.require('lib/api/core/models/requestObject'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), @@ -18,7 +17,6 @@ describe('Test: hotelClerk.countSubscription', function () { before(function (done) { kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); return kuzzle.start(params, {dummy: true}) .then(function () { kuzzle.repositories.role.roles.guest = new Role(); diff --git a/test/api/core/hotelClerck/getChannels.test.js b/test/api/core/hotelClerck/getChannels.test.js index 8b9403d71e..94e4f1f508 100644 --- a/test/api/core/hotelClerck/getChannels.test.js +++ b/test/api/core/hotelClerck/getChannels.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), RequestObject = require.main.require('lib/api/core/models/requestObject'), ResponseObject = require.main.require('lib/api/core/models/responseObject'), params = require('rc')('kuzzle'), @@ -37,7 +36,6 @@ describe('Test: hotelClerk.getChannels', function () { beforeEach(function () { responseObject = new ResponseObject(new RequestObject({collection: 'foo', body: dataGrace})); kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.removeAllListeners(); return kuzzle.start(params, {dummy: true}); diff --git a/test/api/core/hotelClerck/getRealtimeCollections.test.js b/test/api/core/hotelClerck/getRealtimeCollections.test.js index 85f22bb7e4..84545ad593 100644 --- a/test/api/core/hotelClerck/getRealtimeCollections.test.js +++ b/test/api/core/hotelClerck/getRealtimeCollections.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), RequestObject = require.main.require('lib/api/core/models/requestObject'), @@ -12,8 +11,6 @@ describe('Test: hotelClerk.getRealtimeCollections', function () { beforeEach(function () { kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); - return kuzzle.start(params, {dummy: true}); }); diff --git a/test/api/core/hotelClerck/listSubscriptions.test.js b/test/api/core/hotelClerck/listSubscriptions.test.js index 79e51a9967..730292f57f 100644 --- a/test/api/core/hotelClerck/listSubscriptions.test.js +++ b/test/api/core/hotelClerck/listSubscriptions.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), RequestObject = require.main.require('lib/api/core/models/requestObject'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), @@ -31,7 +30,6 @@ describe('Test: hotelClerk.addSubscription', function () { beforeEach(function (done) { require.cache = {}; kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.removeAllListeners(); return kuzzle.start(params, {dummy: true}) diff --git a/test/api/core/hotelClerck/removeAllRooms.test.js b/test/api/core/hotelClerck/removeAllRooms.test.js index 03722bc51c..15e44844be 100644 --- a/test/api/core/hotelClerck/removeAllRooms.test.js +++ b/test/api/core/hotelClerck/removeAllRooms.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), RequestObject = require.main.require('lib/api/core/models/requestObject'), BadRequestError = require.main.require('lib/api/core/errors/badRequestError'), NotFoundError = require.main.require('lib/api/core/errors/notFoundError'), @@ -37,7 +36,6 @@ describe('Test: hotelClerk.removeRooms', function () { beforeEach(function (done) { require.cache = {}; kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.removeAllListeners(); return kuzzle.start(params, {dummy: true}) diff --git a/test/api/core/hotelClerck/removeCustomerFromAllRooms.test.js b/test/api/core/hotelClerck/removeCustomerFromAllRooms.test.js index b0d0ce0c61..72c433a003 100644 --- a/test/api/core/hotelClerck/removeCustomerFromAllRooms.test.js +++ b/test/api/core/hotelClerck/removeCustomerFromAllRooms.test.js @@ -1,7 +1,6 @@ var should = require('should'), q = require('q'), - winston = require('winston'), RequestObject = require.main.require('lib/api/core/models/requestObject'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), @@ -42,7 +41,6 @@ describe('Test: hotelClerk.removeCustomerFromAllRooms', function () { beforeEach(function (done) { kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.removeAllListeners(); kuzzle.start(params, {dummy: true}) .then(function () { diff --git a/test/api/core/hotelClerck/removeSubscription.test.js b/test/api/core/hotelClerck/removeSubscription.test.js index 5844057e9d..d4979dd21f 100644 --- a/test/api/core/hotelClerck/removeSubscription.test.js +++ b/test/api/core/hotelClerck/removeSubscription.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), RequestObject = require.main.require('lib/api/core/models/requestObject'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), @@ -62,7 +61,6 @@ describe('Test: hotelClerk.removeSubscription', function () { beforeEach(function (done) { notified = null; kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.removeAllListeners(); kuzzle.start(params, {dummy: true}) .then(function () { @@ -179,27 +177,16 @@ describe('Test: hotelClerk.removeSubscription', function () { }); }); - it('should call a function leave when the type is websocket', function () { - var leavedRooms = []; - - connection.type = 'websocket'; - kuzzle.io = { - sockets: { - connected: { - connectionid: { - leave: function (channel) { - leavedRooms.push(channel); - } - } - } - } - }; - kuzzle.notifier = {notify: function () {}}; + it('should trigger a protocol:leaveChannel hook', function (done) { + this.timeout(50); - return kuzzle.hotelClerk.removeSubscription(unsubscribeRequest, context) - .then(function () { - should(leavedRooms).containEql(channel); - delete connection.type; - }); + kuzzle.once('protocol:leaveChannel', (data) => { + should(data).be.an.Object(); + should(data.channel).be.a.String(); + should(data.id).be.eql(context.connection.id); + done(); + }); + + kuzzle.hotelClerk.removeSubscription(unsubscribeRequest, context); }); }); diff --git a/test/api/core/models/realTimeResponseObject.test.js b/test/api/core/models/realTimeResponseObject.test.js index 2ef7befd2e..b776b0bfd2 100644 --- a/test/api/core/models/realTimeResponseObject.test.js +++ b/test/api/core/models/realTimeResponseObject.test.js @@ -3,7 +3,6 @@ */ var should = require('should'), - winston = require('winston'), rewire = require('rewire'), uuid = require('node-uuid'), RequestObject = require.main.require('lib/api/core/models/requestObject'), diff --git a/test/api/core/models/requestObject.test.js b/test/api/core/models/requestObject.test.js index 42d1cac76e..9cfe9bccd1 100644 --- a/test/api/core/models/requestObject.test.js +++ b/test/api/core/models/requestObject.test.js @@ -3,7 +3,6 @@ */ var should = require('should'), - winston = require('winston'), rewire = require('rewire'), uuid = require('node-uuid'), RequestObject = require.main.require('lib/api/core/models/requestObject'), diff --git a/test/api/core/models/responseObject.test.js b/test/api/core/models/responseObject.test.js index 7737181ed9..e7092590bd 100644 --- a/test/api/core/models/responseObject.test.js +++ b/test/api/core/models/responseObject.test.js @@ -4,7 +4,6 @@ var should = require('should'), async = require('async'), - winston = require('winston'), rewire = require('rewire'), uuid = require('node-uuid'), RequestObject = require.main.require('lib/api/core/models/requestObject'), diff --git a/test/api/core/notifier/checkNewRoutes.test.js b/test/api/core/notifier/checkNewRoutes.test.js index 4f70278e32..4893f2b54a 100644 --- a/test/api/core/notifier/checkNewRoutes.test.js +++ b/test/api/core/notifier/checkNewRoutes.test.js @@ -6,7 +6,6 @@ */ var should = require('should'), - winston = require('winston'), _ = require('lodash'), rewire = require('rewire'), RequestObject = require.main.require('lib/api/core/models/requestObject'), @@ -50,8 +49,6 @@ describe('Test: notifier.checkNewRoutes', function () { before(function () { kuzzle = new Kuzzle(); - - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); return kuzzle.start(params, {dummy: true}); }); diff --git a/test/api/core/notifier/init.test.js b/test/api/core/notifier/init.test.js index b4fa3f4aca..1be6a5c8eb 100644 --- a/test/api/core/notifier/init.test.js +++ b/test/api/core/notifier/init.test.js @@ -4,7 +4,6 @@ */ var should = require('should'), - winston = require('winston'), rewire = require('rewire'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), @@ -18,7 +17,6 @@ describe('Test: notifier.init', function () { before(function () { kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); return kuzzle.start(params, {dummy: true}); }); diff --git a/test/api/core/notifier/notify.test.js b/test/api/core/notifier/notify.test.js index a1064969df..67535a9e70 100644 --- a/test/api/core/notifier/notify.test.js +++ b/test/api/core/notifier/notify.test.js @@ -5,7 +5,6 @@ */ var should = require('should'), - winston = require('winston'), rewire = require('rewire'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), @@ -19,7 +18,6 @@ describe('Test: notifier.notify', function () { before(function () { kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); return kuzzle.start(params, {dummy: true}); }); diff --git a/test/api/core/notifier/notifyDocumentCreate.test.js b/test/api/core/notifier/notifyDocumentCreate.test.js index 1b52680996..06a9c88e19 100644 --- a/test/api/core/notifier/notifyDocumentCreate.test.js +++ b/test/api/core/notifier/notifyDocumentCreate.test.js @@ -8,7 +8,6 @@ var should = require('should'), q = require('q'), - winston = require('winston'), rewire = require('rewire'), RequestObject = require.main.require('lib/api/core/models/requestObject'), ResponseObject = require.main.require('lib/api/core/models/responseObject'), @@ -46,7 +45,6 @@ describe('Test: notifier.notifyDocumentCreate', function () { 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.notificationCache = mockupCacheService; diff --git a/test/api/core/notifier/notifyDocumentDelete.test.js b/test/api/core/notifier/notifyDocumentDelete.test.js index 8ca2f2a585..94dedaa44c 100644 --- a/test/api/core/notifier/notifyDocumentDelete.test.js +++ b/test/api/core/notifier/notifyDocumentDelete.test.js @@ -7,7 +7,6 @@ */ var should = require('should'), - winston = require('winston'), rewire = require('rewire'), RequestObject = require.main.require('lib/api/core/models/requestObject'), ResponseObject = require.main.require('lib/api/core/models/responseObject'), @@ -50,7 +49,6 @@ describe('Test: notifier.notifyDocumentDelete', function () { 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.notificationCache = mockupCacheService; diff --git a/test/api/core/notifier/notifyDocumentUpdate.test.js b/test/api/core/notifier/notifyDocumentUpdate.test.js index 7a84e695c3..9f1018ab7d 100644 --- a/test/api/core/notifier/notifyDocumentUpdate.test.js +++ b/test/api/core/notifier/notifyDocumentUpdate.test.js @@ -7,7 +7,6 @@ */ var should = require('should'), - winston = require('winston'), rewire = require('rewire'), RequestObject = require.main.require('lib/api/core/models/requestObject'), ResponseObject = require.main.require('lib/api/core/models/responseObject'), @@ -84,7 +83,6 @@ describe('Test: notifier.notifyDocumentUpdate', function () { 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.notificationCache = mockupCacheService; diff --git a/test/api/core/notifier/publish.test.js b/test/api/core/notifier/publish.test.js index 0362e7f5f8..2be48f0c4c 100644 --- a/test/api/core/notifier/publish.test.js +++ b/test/api/core/notifier/publish.test.js @@ -5,7 +5,6 @@ */ var should = require('should'), - winston = require('winston'), rewire = require('rewire'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), @@ -27,7 +26,6 @@ describe('Test: notifier.publish', function () { before(function (done) { kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.start(params, {dummy: true}) .then(() => { kuzzle.services.list.notificationCache = { diff --git a/test/api/core/notifier/send.test.js b/test/api/core/notifier/send.test.js index 75e5a25c30..49db82a933 100644 --- a/test/api/core/notifier/send.test.js +++ b/test/api/core/notifier/send.test.js @@ -5,7 +5,6 @@ */ var should = require('should'), - winston = require('winston'), rewire = require('rewire'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), @@ -13,58 +12,36 @@ var require('should-promised'); -var mockupio = { - emitted: false, - id: undefined, - room: undefined, - response: undefined, - - init: function () { - this.emitted = false; - this.id = this.room = this.response = undefined; - }, - - to: function (connectionId) { this.id = connectionId; return this; }, - - emit: function (room, response) { - this.room = room; - this.response = response; - this.emitted = true; - } -}; - describe('Test: notifier.send', function () { var kuzzle; - before(function (done) { + before(function () { kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); - kuzzle.start(params, {dummy: true}) - .then(function () { - kuzzle.io = mockupio; - done(); - }); + return kuzzle.start(params, {dummy: true}); }); - it('should broadcast to channels if no connection is provided', function () { + it('should emit a protocol:broadcast hook on channels to be notified', function (done) { var room = 'foo', response = 'bar', channel = 'stubChannel'; - mockupio.init(); + this.timeout(50); + kuzzle.hotelClerk.getChannels = function () { return [channel]; }; kuzzle.services.list.mqBroker.addExchange = function (replyTopic, msg) { should(replyTopic).be.exactly(channel); should(msg).be.exactly(response); }; - (Notifier.__get__('send')).call(kuzzle, room, response); + kuzzle.once('protocol:broadcast', (data) => { + should(data).be.an.Object(); + should(data.channel).be.eql(channel); + should(data.payload).be.eql(response); + done(); + }); - should(mockupio.emitted).be.true(); - should(mockupio.id).be.exactly(channel); - should(mockupio.room).be.exactly(channel); - should(mockupio.response).be.exactly(response); + (Notifier.__get__('send')).call(kuzzle, room, response); }); }); diff --git a/test/api/core/notifier/workerNotification.test.js b/test/api/core/notifier/workerNotification.test.js index c77a4d7b15..2902f58192 100644 --- a/test/api/core/notifier/workerNotification.test.js +++ b/test/api/core/notifier/workerNotification.test.js @@ -8,7 +8,6 @@ */ var should = require('should'), - winston = require('winston'), rewire = require('rewire'), RequestObject = require.main.require('lib/api/core/models/requestObject'), ResponseObject = require.main.require('lib/api/core/models/responseObject'), @@ -122,7 +121,6 @@ describe('Test: notifier.workerNotification', function () { this.timeout(1000); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.start(params, {dummy: true}).then(function () { responseObject.action = 'create'; responseObject.collection = false; diff --git a/test/api/core/pluginsManager/constructList.test.js b/test/api/core/pluginsManager/constructList.test.js index 0b2e24b1f5..e4acbf2108 100644 --- a/test/api/core/pluginsManager/constructList.test.js +++ b/test/api/core/pluginsManager/constructList.test.js @@ -29,7 +29,7 @@ describe('Plugins manager constructList', function () { }, mergedPlugins; - mergedPlugins = constructList(defaultPlugins, customPlugins, true); + mergedPlugins = constructList(defaultPlugins, customPlugins); should(mergedPlugins.foo).be.Object(); should(mergedPlugins.bar).be.Object(); }); @@ -54,7 +54,7 @@ describe('Plugins manager constructList', function () { }, mergedPlugins; - mergedPlugins = constructList(defaultPlugins, customPlugins, true); + mergedPlugins = constructList(defaultPlugins, customPlugins); should(mergedPlugins.foo.config).be.Object(); should(mergedPlugins.foo.config).have.property('bar'); @@ -92,7 +92,7 @@ describe('Plugins manager constructList', function () { }, mergedPlugins; - mergedPlugins = constructList(defaultPlugins, customPlugins, true); + mergedPlugins = constructList(defaultPlugins, customPlugins); should(mergedPlugins.foo.config).be.Object(); should(mergedPlugins.foo.config).have.property('bar'); @@ -131,7 +131,7 @@ describe('Plugins manager constructList', function () { }, mergedPlugins; - mergedPlugins = constructList(defaultPlugins, customPlugins, true); + mergedPlugins = constructList(defaultPlugins, customPlugins); should(mergedPlugins.foo.config).be.Object(); should(mergedPlugins.foo.config).have.property('bar'); @@ -154,9 +154,74 @@ describe('Plugins manager constructList', function () { defaultPlugins = {}, mergedPlugins; - mergedPlugins = constructList(defaultPlugins, customPlugins, true); + mergedPlugins = constructList(defaultPlugins, customPlugins); should(mergedPlugins.foo.config).be.Object(); should(mergedPlugins.foo.config).have.property('bar'); }); + + it('should add server plugins only on server instances', function () { + var + plugins, + defaultPlugins = { + serverPlugin: { + defaultConfig: { + loadedBy: 'server' + } + }, + workerPlugin: { + defaultConfig: { + loadedBy: 'worker' + } + } + }; + + plugins = constructList(defaultPlugins, {}, true); + + should(plugins.serverPlugin).be.an.Object(); + should(plugins.workerPlugin).be.undefined(); + }); + + it('should run worker plugins only on worker instances', function () { + var + plugins, + defaultPlugins = { + serverPlugin: { + defaultConfig: { + loadedBy: 'server' + } + }, + workerPlugin: { + defaultConfig: { + loadedBy: 'worker' + } + } + }; + + plugins = constructList(defaultPlugins, {}, false); + + should(plugins.serverPlugin).be.undefined(); + should(plugins.workerPlugin).be.an.Object(); + }); + + it('should always start plugins with loadedBy="all"', function () { + var + plugins, + defaultPlugins = { + serverPlugin: { + defaultConfig: { + loadedBy: 'all' + } + }, + workerPlugin: { + defaultConfig: { + } + } + }; + + plugins = constructList(defaultPlugins, {}, true); + + should(plugins.serverPlugin).be.an.Object(); + should(plugins.workerPlugin).be.an.Object(); + }); }); \ No newline at end of file diff --git a/test/api/core/pluginsManager/init.test.js b/test/api/core/pluginsManager/init.test.js new file mode 100644 index 0000000000..b4eadacb71 --- /dev/null +++ b/test/api/core/pluginsManager/init.test.js @@ -0,0 +1,136 @@ +var + should = require('should'), + rewire = require('rewire'), + PluginsManager = rewire('../../../../lib/api/core/pluginsManager'); + +describe('Plugins manager initialization', function () { + var + kuzzle = { + config: { + pluginsManager: {} + } + }, + installed = false, + configWritten = false, + locked = false; + + before(function () { + PluginsManager.__set__('console', { + log: () => {}, + error: () => {} + }); + + PluginsManager.__set__('installPlugins', function () { + installed = true; + return true; + }); + + PluginsManager.__set__('loadPlugins', function () {}); + + PluginsManager.__set__('fs', { + writeFileSync: function () { + configWritten = true; + return false; + }, + existsSync: function (path) { + // prevents "require" on the non-existent custom plugins file + if (path.includes('customPlugins')) { + return false; + } + + return locked; + } + }); + }); + + beforeEach(function () { + installed = false; + configWritten = false; + locked = false; + }); + + it('should exit Kuzzle if nodejs does not support sync fs methods', function (done) { + var exitted = false; + + PluginsManager.__with__({ + childProcess: { + /*jshint -W001 */ + hasOwnProperty: () => { return false; } + }, + process: { + exit: () => exitted = true + } + })(function () { + var pluginManager = new PluginsManager(kuzzle); + pluginManager.init(true, false) + .then(() => { + should(exitted).be.true(); + done(); + }) + .catch(err => done(err)); + }); + }); + + it('should wait for the lock to be released before installing plugins', function (done) { + var + released = false; + + this.timeout(100); + + PluginsManager.__with__({ + lockfile: { + lock: function (foo, bar, cb) { + setTimeout(function () { + if (installed) { + return done(new Error('Plugin installation started before locks were released')); + } + + cb(null, function () { released = true; }); + }, 20); + } + } + })(function () { + var pluginManager = new PluginsManager(kuzzle); + pluginManager.init(true, false) + .then(() => { + should(installed).be.true(); + should(released).be.true(); + should(configWritten).be.true(); + done(); + }) + .catch(err => done(err)); + }); + }); + + it('should not install plugins if locked by another instance', function (done) { + var + released = false; + + this.timeout(100); + locked = true; + + PluginsManager.__with__({ + lockfile: { + lock: function (foo, bar, cb) { + setTimeout(function () { + if (installed) { + return done(new Error('Plugin installation started before locks were released')); + } + + cb(null, function () { released = true; }); + }, 20); + } + } + })(function () { + var pluginManager = new PluginsManager(kuzzle); + pluginManager.init(true, false) + .then(() => { + should(installed).be.false(); + should(released).be.true(); + should(configWritten).be.false(); + done(); + }) + .catch(err => done(err)); + }); + }); +}); diff --git a/test/api/core/pluginsManager/installPlugins.test.js b/test/api/core/pluginsManager/installPlugins.test.js index 85b010d56b..a3d51f3efe 100644 --- a/test/api/core/pluginsManager/installPlugins.test.js +++ b/test/api/core/pluginsManager/installPlugins.test.js @@ -10,7 +10,7 @@ describe('Test plugins manager installation', function () { before(function () { PluginsManager.__set__('initConfig', function () {}); PluginsManager.__set__('npmInstall', function () {}); - PluginsManager.__set__('kuzzle', {log: {info: function () {}, error: function () {}}}); + PluginsManager.__set__('console', {log: function () {}, error: function () {}}); installPlugins = PluginsManager.__get__('installPlugins'); }); diff --git a/test/api/core/pluginsManager/run.test.js b/test/api/core/pluginsManager/run.test.js index 101e1fa20e..2825b05689 100644 --- a/test/api/core/pluginsManager/run.test.js +++ b/test/api/core/pluginsManager/run.test.js @@ -27,6 +27,10 @@ describe('Test plugins manager run', function () { kuzzle, pluginsManager; + before(function () { + PluginsManager.__set__('console', {log: function () {}, error: function () {}}); + }); + beforeEach(() => { kuzzle = new EventEmitter({ wildcard: true, @@ -71,6 +75,7 @@ describe('Test plugins manager run', function () { done(); } }, + config: {}, activated: true }]; @@ -89,6 +94,7 @@ describe('Test plugins manager run', function () { done(); } }, + config: {}, activated: true }]; @@ -110,6 +116,7 @@ describe('Test plugins manager run', function () { callback(null, object); } }, + config: {}, activated: true }]; @@ -138,6 +145,7 @@ describe('Test plugins manager run', function () { callback(); } }, + config: {}, activated: true }]; @@ -163,6 +171,7 @@ describe('Test plugins manager run', function () { callback(true); } }, + config: {}, activated: true }]; @@ -183,6 +192,7 @@ describe('Test plugins manager run', function () { hooks: {'log:warn': 'warn'}, warn: (msg) => { warnings.push(msg); } }, + config: {}, activated: true }); @@ -252,6 +262,7 @@ describe('Test plugins manager run', function () { }, FooController: function(){done();} }, + config: {}, activated: true } }; @@ -286,6 +297,7 @@ describe('Test plugins manager run', function () { done(); } }, + config: {}, activated: true }]; @@ -298,11 +310,13 @@ describe('Test plugins manager run', function () { myplugin: { object: { init: function () {}, + config: {}, routes: [ {verb: 'get', url: '/bar/:name', controller: 'foo', action: 'bar'}, {verb: 'post', url: '/bar', controller: 'foo', action: 'bar'} ] }, + config: {}, activated: true } }; @@ -320,5 +334,4 @@ describe('Test plugins manager run', function () { .be.equal(pluginsManager.routes[0].action) .and.be.equal('bar'); }); - }); diff --git a/test/api/core/pluginsManager/trigger.test.js b/test/api/core/pluginsManager/trigger.test.js index 8c724ff42f..0123219679 100644 --- a/test/api/core/pluginsManager/trigger.test.js +++ b/test/api/core/pluginsManager/trigger.test.js @@ -1,10 +1,14 @@ var should = require('should'), params = require('rc')('kuzzle'), - PluginsManager = require.main.require('lib/api/core/pluginsManager'), + rewire = require('rewire'), + PluginsManager = rewire('../../../../lib/api/core/pluginsManager'), EventEmitter = require('eventemitter2').EventEmitter2; describe('Test plugins manager trigger', function () { + before(function () { + PluginsManager.__set__('console', {log: function () {}, error: function () {}}); + }); it('should trigger hooks event', function (done) { var @@ -29,6 +33,7 @@ describe('Test plugins manager trigger', function () { done(); } }, + config: {}, activated: true }]; diff --git a/test/api/core/sandbox/sandbox.test.js b/test/api/core/sandbox/sandbox.test.js index 8fb4cd821e..ab06d3c171 100644 --- a/test/api/core/sandbox/sandbox.test.js +++ b/test/api/core/sandbox/sandbox.test.js @@ -106,8 +106,8 @@ describe('Test: sandbox/sandboxTest', () => { revert = LocalSandbox.__set__({ process: { - debugPort: 17512, - execArgv: ['--debug-port=17512'] + debugPort: 17511, + execArgv: ['--debug-port=17511'] } }); diff --git a/test/api/core/servers.test.js b/test/api/core/servers.test.js index 981fab3207..169ad40d88 100644 --- a/test/api/core/servers.test.js +++ b/test/api/core/servers.test.js @@ -3,7 +3,6 @@ */ var should = require('should'), - winston = require('winston'), http = require('http'), rewire = require('rewire'), params = require('rc')('kuzzle'), @@ -17,30 +16,24 @@ describe('Test: core/servers', function () { kuzzle, port = 6667, httpServer = false, - websocketServer = false, mqServer = false, restRedirected = false; before(function (done) { + this.timeout(200); kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.start(params, {dummy: true}) .then(function () { kuzzle.router.initRouterHttp = function () { httpServer = true; }; - kuzzle.router.routeWebsocket = function () { websocketServer = true; }; kuzzle.router.routeMQListener = function () { mqServer = true; }; kuzzle.router.routeHttp = function () { restRedirected = true; }; - Servers.initAll(kuzzle, { port: port }); - kuzzle.io.emit('connection', {}); - done(); }); }); it('should register all three servers at initialization', function () { should(httpServer).be.true(); - should(websocketServer).be.true(); should(mqServer).be.true(); }); diff --git a/test/api/core/statistics.test.js b/test/api/core/statistics.test.js index 667a028ab5..9b8db82d96 100644 --- a/test/api/core/statistics.test.js +++ b/test/api/core/statistics.test.js @@ -3,7 +3,6 @@ */ var should = require('should'), - winston = require('winston'), rewire = require('rewire'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), @@ -27,7 +26,6 @@ describe('Test: statistics core component', function () { before(function (done) { kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.start(params, {dummy: true}) .then(() => { return kuzzle.services.list.statsCache.init(kuzzle, {service: 'statsCache'}); diff --git a/test/api/core/workerListener.test.js b/test/api/core/workerListener.test.js index 5d4efb1801..898a2834f4 100644 --- a/test/api/core/workerListener.test.js +++ b/test/api/core/workerListener.test.js @@ -3,7 +3,6 @@ */ var should = require('should'), - winston = require('winston'), rewire = require('rewire'), uuid = require('node-uuid'), params = require('rc')('kuzzle'), @@ -30,7 +29,6 @@ describe('Test: workerListener', function () { 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.broker.listen = function (room, cb) { diff --git a/test/api/dsl/index/removeRoom.test.js b/test/api/dsl/index/removeRoom.test.js index 7d2467eef9..dc11bf5c82 100644 --- a/test/api/dsl/index/removeRoom.test.js +++ b/test/api/dsl/index/removeRoom.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), rewire = require('rewire'), RequestObject = require.main.require('lib/api/core/models/requestObject'), params = require('rc')('kuzzle'), @@ -34,7 +33,6 @@ describe('Test removeRoom function index.js file from DSL', function () { beforeEach(function (done) { kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.removeAllListeners(); kuzzle.start(params, {dummy: true}) .then(function () { diff --git a/test/api/dsl/index/testFilters.test.js b/test/api/dsl/index/testFilters.test.js index d18f5f3766..ba8bb9bd51 100644 --- a/test/api/dsl/index/testFilters.test.js +++ b/test/api/dsl/index/testFilters.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), rewire = require('rewire'), RequestObject = require.main.require('lib/api/core/models/requestObject'), params = require('rc')('kuzzle'), @@ -105,7 +104,6 @@ describe('Test: dsl.testFilters', function () { 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 () { diff --git a/test/api/dsl/index/testRooms.test.js b/test/api/dsl/index/testRooms.test.js index 126949017a..ecf26e5c9a 100644 --- a/test/api/dsl/index/testRooms.test.js +++ b/test/api/dsl/index/testRooms.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), rewire = require('rewire'), @@ -16,7 +15,6 @@ describe('Test: dsl.removeRoomFromFields', function () { before(function () { kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); return kuzzle.start(params, {dummy: true}); }); diff --git a/test/hooks/index.test.js b/test/hooks/index.test.js index e3e16165d6..e3e9ea4781 100644 --- a/test/hooks/index.test.js +++ b/test/hooks/index.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), params = require('rc')('kuzzle'), _ = require('lodash'), Kuzzle = require.main.require('lib/api/Kuzzle'); @@ -22,7 +21,6 @@ describe('Test main file for hooks managers', function () { beforeEach(function () { kuzzle = new Kuzzle(); kuzzle.removeAllListeners(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); }); it('should be rejected on init when a hook is undefined in config', function () { diff --git a/test/services/broker.test.js b/test/services/broker.test.js index 36908a5324..3e84c39777 100644 --- a/test/services/broker.test.js +++ b/test/services/broker.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), rewire = require('rewire'), @@ -18,7 +17,6 @@ describe('Testing: broker service', function () { 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 () { Broker = rewire('../../lib/services/' + kuzzle.config.services.broker); diff --git a/test/services/implementations/internalbroker.test.js b/test/services/implementations/internalbroker.test.js index 84632670b6..bf8391fc44 100644 --- a/test/services/implementations/internalbroker.test.js +++ b/test/services/implementations/internalbroker.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), rewire = require('rewire'), IPCBroker = rewire('../../../lib/services/internalbroker'), params = require('rc')('kuzzle'), @@ -26,7 +25,6 @@ describe('Test: Internal Broker service ', function () { port: '6666' }; - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); brokerServer = new IPCBroker(kuzzle, { isServer: true }); brokerClient = new IPCBroker(kuzzle, { isServer: false }); diff --git a/test/services/init.test.js b/test/services/init.test.js index 1541e7f347..431744c9bd 100644 --- a/test/services/init.test.js +++ b/test/services/init.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'); @@ -11,64 +10,69 @@ describe('Test service initialization function', function () { beforeEach(function () { kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.removeAllListeners(); }); it('should build an internal broker service with correct methods', function () { - kuzzle.start(params, {dummy: true}); - - should(kuzzle.services.list.broker).be.an.Object().and.not.be.empty(); - should(kuzzle.services.list.broker.init).be.a.Function(); - should(kuzzle.services.list.broker.add).be.a.Function(); - should(kuzzle.services.list.broker.broadcast).be.a.Function(); - should(kuzzle.services.list.broker.listen).be.a.Function(); - should(kuzzle.services.list.broker.listenOnce).be.a.Function(); - should(kuzzle.services.list.broker.close).be.a.Function(); + return kuzzle.start(params, {dummy: true}) + .then(() => { + should(kuzzle.services.list.broker).be.an.Object().and.not.be.empty(); + should(kuzzle.services.list.broker.init).be.a.Function(); + should(kuzzle.services.list.broker.add).be.a.Function(); + should(kuzzle.services.list.broker.broadcast).be.a.Function(); + should(kuzzle.services.list.broker.listen).be.a.Function(); + should(kuzzle.services.list.broker.listenOnce).be.a.Function(); + should(kuzzle.services.list.broker.close).be.a.Function(); + }); }); it('should build a MQ broker service with correct methods', function () { - kuzzle.start(params, {dummy: true}); - - should(kuzzle.services.list.mqBroker).be.an.Object().and.not.be.empty(); - should(kuzzle.services.list.mqBroker.init).be.a.Function(); - should(kuzzle.services.list.mqBroker.toggle).be.a.Function(); - should(kuzzle.services.list.mqBroker.add).be.a.Function(); - should(kuzzle.services.list.mqBroker.addExchange).be.a.Function(); - should(kuzzle.services.list.mqBroker.listenExchange).be.a.Function(); - should(kuzzle.services.list.mqBroker.replyTo).be.a.Function(); - should(kuzzle.services.list.mqBroker.listen).be.a.Function(); - should(kuzzle.services.list.mqBroker.listenOnce).be.a.Function(); - should(kuzzle.services.list.mqBroker.close).be.a.Function(); + return kuzzle.start(params, {dummy: true}) + .then(() => { + should(kuzzle.services.list.mqBroker).be.an.Object().and.not.be.empty(); + should(kuzzle.services.list.mqBroker.init).be.a.Function(); + should(kuzzle.services.list.mqBroker.toggle).be.a.Function(); + should(kuzzle.services.list.mqBroker.add).be.a.Function(); + should(kuzzle.services.list.mqBroker.addExchange).be.a.Function(); + should(kuzzle.services.list.mqBroker.listenExchange).be.a.Function(); + should(kuzzle.services.list.mqBroker.replyTo).be.a.Function(); + should(kuzzle.services.list.mqBroker.listen).be.a.Function(); + should(kuzzle.services.list.mqBroker.listenOnce).be.a.Function(); + should(kuzzle.services.list.mqBroker.close).be.a.Function(); + }); }); it('should build a readEngine service with correct methods', function () { - kuzzle.start(params, {dummy: true}); - - should(kuzzle.services.list.readEngine).be.an.Object(); - should(kuzzle.services.list.readEngine.init).be.a.Function(); - should(kuzzle.services.list.readEngine.search).be.a.Function(); - should(kuzzle.services.list.readEngine.get).be.a.Function(); + return kuzzle.start(params, {dummy: true}) + .then(() => { + should(kuzzle.services.list.readEngine).be.an.Object(); + should(kuzzle.services.list.readEngine.init).be.a.Function(); + should(kuzzle.services.list.readEngine.search).be.a.Function(); + should(kuzzle.services.list.readEngine.get).be.a.Function(); + }); }); it('should build a writeEngine service with correct methods', function () { - kuzzle.start(params, {dummy: true}); - - should(kuzzle.services.list.writeEngine).be.an.Object(); - should(kuzzle.services.list.writeEngine.init).be.a.Function(); - should(kuzzle.services.list.writeEngine.create).be.a.Function(); - should(kuzzle.services.list.writeEngine.update).be.a.Function(); - should(kuzzle.services.list.writeEngine.deleteByQuery).be.a.Function(); - should(kuzzle.services.list.writeEngine.deleteCollection).be.a.Function(); - should(kuzzle.services.list.writeEngine.import).be.a.Function(); + return kuzzle.start(params, {dummy: true}) + .then(() => { + should(kuzzle.services.list.writeEngine).be.an.Object(); + should(kuzzle.services.list.writeEngine.init).be.a.Function(); + should(kuzzle.services.list.writeEngine.create).be.a.Function(); + should(kuzzle.services.list.writeEngine.update).be.a.Function(); + should(kuzzle.services.list.writeEngine.deleteByQuery).be.a.Function(); + should(kuzzle.services.list.writeEngine.deleteCollection).be.a.Function(); + should(kuzzle.services.list.writeEngine.import).be.a.Function(); + }); }); it('should build a cache service', function () { - kuzzle.start(params, {dummy: true}); - should(kuzzle.services.list.notificationCache).be.an.Object(); - should(kuzzle.services.list.notificationCache.add).be.a.Function(); - should(kuzzle.services.list.notificationCache.remove).be.a.Function(); - should(kuzzle.services.list.notificationCache.search).be.a.Function(); + return kuzzle.start(params, {dummy: true}) + .then(() => { + should(kuzzle.services.list.notificationCache).be.an.Object(); + should(kuzzle.services.list.notificationCache.add).be.a.Function(); + should(kuzzle.services.list.notificationCache.remove).be.a.Function(); + should(kuzzle.services.list.notificationCache.search).be.a.Function(); + }); }); it('should not init services in blacklist', function () { diff --git a/test/services/redis.test.js b/test/services/redis.test.js index d683b42772..05b29db88d 100644 --- a/test/services/redis.test.js +++ b/test/services/redis.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), q = require('q'), params = require('rc')('kuzzle'), Redis = require.main.require('lib/services/redis'), @@ -16,7 +15,6 @@ describe('Test redis service', function () { before(function (done) { kuzzle = new Kuzzle(); - kuzzle.log = new (winston.Logger)({transports: [new (winston.transports.Console)({level: 'silent'})]}); kuzzle.start(params, {dummy: true}) .then(() => { kuzzle.config.cache.databases.push(dbname); diff --git a/test/services/remoteActions.test.js b/test/services/remoteActions.test.js index e5b8b8d1dd..f147e83859 100644 --- a/test/services/remoteActions.test.js +++ b/test/services/remoteActions.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), rewire = require('rewire'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), @@ -16,7 +15,6 @@ describe('Testing: Remote Actions service', function () { 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 () { remoteActions = new RemoteActions(kuzzle); diff --git a/test/workers/index.test.js b/test/workers/index.test.js index 9cea26e801..ae3492de44 100644 --- a/test/workers/index.test.js +++ b/test/workers/index.test.js @@ -2,7 +2,6 @@ var should = require('should'), _ = require('lodash'), q = require('q'), - winston = require('winston'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), WLoader = require('../../lib/workers/index'); @@ -15,7 +14,6 @@ describe('Testing: workers loader', function () { 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 () { Object.keys(kuzzle.config.workers).forEach(function (workerGroup) { diff --git a/test/workers/write.test.js b/test/workers/write.test.js index c6a2f75dd3..0968d8ba0a 100644 --- a/test/workers/write.test.js +++ b/test/workers/write.test.js @@ -1,6 +1,5 @@ var should = require('should'), - winston = require('winston'), rewire = require('rewire'), params = require('rc')('kuzzle'), Kuzzle = require.main.require('lib/api/Kuzzle'), @@ -16,7 +15,6 @@ describe('Testing: write worker', function () { 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 () { diff --git a/vagrant/vagrant.yml b/vagrant/vagrant.yml index eae31c10db..a6d0cfb276 100644 --- a/vagrant/vagrant.yml +++ b/vagrant/vagrant.yml @@ -25,7 +25,7 @@ virtualmachine: # List of ports to be forwarded to your host: # override with empty array if you do not want to forward ports # (in that case, you will need a private IP address to use Kuzzle - see above) - forwarded_port: { 7512: 7512, 8081: 7512, 1883: 1883, 5672: 5672, 15672: 15672, 61613: 61613 } + forwarded_port: { 7511: 7511, 8081: 7511, 7512: 7512, 1883: 1883, 5672: 5672, 15672: 15672, 61613: 61613 } ### END Network Settings