Comparing version 0.0.6 to 0.0.7
# Changelog | ||
## unreleased | ||
## [0.0.7](https://github.com/sloki-project/sloki/milestone/5) - UNRELEASED | ||
### Added | ||
* add method `versions` [#19](https://github.com/sloki-project/sloki/issues/19) | ||
* add alias property for all methods [#21](https://github.com/sloki-project/sloki/issues/21) | ||
* add binary tcp protocol [#22](https://github.com/sloki-project/sloki/issues/22) | ||
* add manual garbage collector [#23](https://github.com/sloki-project/sloki/issues/23) [#24](https://github.com/sloki-project/sloki/issues/24) | ||
* TLS: generate self signed certificate when sloki start [#26](https://github.com/sloki-project/sloki/issues/26) | ||
* TLS: implement both jsonrpc and binary server [#27](https://github.com/sloki-project/sloki/issues/27) | ||
### Changed | ||
* switch to named parameter in jsonrpc layer [#20](https://github.com/sloki-project/sloki/issues/20) | ||
* no response needed if attribute "nr" exists in params (lazy mode) | ||
* listen on multiple port per transport/protocol by default [#25](https://github.com/sloki-project/sloki/issues/25) | ||
## [0.0.6](https://github.com/sloki-project/sloki/milestone/4) - 2019-02-09 | ||
### Added | ||
* add method wait for testing purpuse [(one sec)](https://github.com/sloki-project/sloki/commit/80c51ac81d18e18794f1782486aec9d8b4166c55) | ||
* add method `wait` for testing purpose [(one sec)](https://github.com/sloki-project/sloki/commit/80c51ac81d18e18794f1782486aec9d8b4166c55) | ||
@@ -25,6 +33,6 @@ ### Changed | ||
### Added | ||
* add method collection/update [#5](https://github.com/sloki-project/sloki/issues/5) | ||
* add method collection/find [#3](https://github.com/sloki-project/sloki/issues/3) | ||
* add method collection/remove [#4](https://github.com/sloki-project/sloki/issues/4) | ||
* add method collection/get [#2](https://github.com/sloki-project/sloki/issues/2) | ||
* add method collection/`update` [#5](https://github.com/sloki-project/sloki/issues/5) | ||
* add method collection/`find` [#3](https://github.com/sloki-project/sloki/issues/3) | ||
* add method collection/`remove` [#4](https://github.com/sloki-project/sloki/issues/4) | ||
* add method collection/`get` [#2](https://github.com/sloki-project/sloki/issues/2) | ||
@@ -44,15 +52,15 @@ ### Changed | ||
* add JSONRPC over tcp layer (jayson) | ||
* add method server/clients | ||
* add method server/methods | ||
* add method server/maxClients | ||
* add method server/quit | ||
* add method server/shutdown | ||
* add method database/db | ||
* add method database/loadDatabase | ||
* add method database/listDatabases | ||
* add method database/saveDatabase | ||
* add method database/addCollection | ||
* add method database/listCollections | ||
* add method collection/get | ||
* add method collection/insert | ||
* add method server/`clients` | ||
* add method server/`methods` | ||
* add method server/`maxClients` | ||
* add method server/`quit` | ||
* add method server/`shutdown` | ||
* add method database/`db` | ||
* add method database/`loadDatabase` | ||
* add method database/`listDatabases` | ||
* add method database/`saveDatabase` | ||
* add method database/`addCollection` | ||
* add method database/`listCollections` | ||
* add method collection/`get` | ||
* add method collection/`insert` | ||
* add tests | ||
@@ -59,0 +67,0 @@ |
{ | ||
"name": "sloki", | ||
"version": "0.0.6", | ||
"version": "0.0.7", | ||
"description": "A NodeJS server for LokiJS", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "node test/index.js" | ||
"test": "node --optimize_for_size --expose_gc test/index.js", | ||
"start": "node --optimize_for_size --max_old_space_size=7360 --expose_gc index.js" | ||
}, | ||
@@ -30,18 +31,17 @@ "bin": { | ||
"dependencies": { | ||
"abrequire": "0.0.4", | ||
"evillogger": "^1.2.2", | ||
"fs-extra": "^7.0.1", | ||
"jayson": "^2.1.1", | ||
"klaw-sync": "^6.0.0", | ||
"async": "2.6.1", | ||
"evillogger": "1.2.2", | ||
"fs-extra": "7.0.1", | ||
"jayson": "2.1.2", | ||
"klaw-sync": "6.0.0", | ||
"lokijs": "franck34/LokiJS", | ||
"pretty-bytes": "^5.1.0", | ||
"sloki-node-client": "0.0.6" | ||
"missive": "3.0.1", | ||
"pretty-bytes": "5.1.0", | ||
"selfsigned": "1.10.4" | ||
}, | ||
"devDependencies": { | ||
"async": "^2.6.1", | ||
"eslint": "^5.13.0", | ||
"tap": "^12.1.1", | ||
"tap-spec": "^5.0.0", | ||
"tape": "^4.9.2" | ||
"eslint": "5.13.0", | ||
"tap-spec": "5.0.0", | ||
"tape": "4.9.2" | ||
} | ||
} |
207
README.md
# Sloki (WORK IN PROGRESS) | ||
A NodeJS Server for [LokiJS](http://lokijs.org/) | ||
[![Join the chat at https://gitter.im/sloki-server/community](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/sloki-server/community) | ||
[![alt CI-badge](https://travis-ci.org/sloki-project/sloki.svg?branch=master)](https://travis-ci.org/sloki-project/sloki) | ||
[![npm version](https://badge.fury.io/js/sloki.svg?v=0)](http://badge.fury.io/js/sloki) | ||
[![Known Vulnerabilities](https://snyk.io/test/github/sloki-project/sloki/badge.svg?targetFile=package.json)](https://snyk.io/test/github/sloki-project/sloki?targetFile=package.json) | ||
[![Dependencies](https://david-dm.org/sloki-project/sloki.svg)](https://david-dm.org/sloki-project/sloki) | ||
[![Dev Dependencies](https://david-dm.org/sloki-project/sloki/dev-status.svg)](https://david-dm.org/sloki-project/sloki?type=dev) | ||
[![Known Vulnerabilities](https://snyk.io/test/github/sloki-project/sloki/badge.svg?targetFile=server/package.json)](https://snyk.io/test/github/sloki-project/sloki?targetFile=server/package.json) | ||
[![Dependencies](https://david-dm.org/sloki-project/sloki.svg?path=server/)](https://david-dm.org/sloki-project/sloki) | ||
[![Dev Dependencies](https://david-dm.org/sloki-project/sloki/dev-status.svg?path=server/)](https://david-dm.org/sloki-project/sloki?type=dev) | ||
----- | ||
## Documentation | ||
1. [Introduction](#1-introduction) | ||
1. [Transports](#1i-transports) | ||
2. [Protocols](#1ii-protocols) | ||
1. [Binary](#1iia-binary-protocol-default) (default) | ||
2. [JSONRPC](#1iib-jsonrpc) | ||
2. [Installation](#4-installation) | ||
----- | ||
## Overview | ||
## 1. Introduction | ||
Sloki make LokiJS ***scalable***. | ||
Sloki is a nodejs server which embed [LokiJS](http://lokijs.org/), a blazing fast in-memory documents database. | ||
Sloki help to make LokiJS ***scalable*** : you can now have multiple processes speaking with LokiJS through Sloki. | ||
* It embed [LokiJS](http://lokijs.org/) | ||
* It expose a [JSONRPC](https://www.jsonrpc.org/) API, thanks to [Jayson](https://github.com/tedeh/jayson) | ||
* It **WILL** support TCP/TLS | ||
* It **MAY** support HTTP/HTTPS | ||
A possible architecture using sloki : | ||
``` | ||
+----------------------------+ TCP / Binary +-----------------------------------+ | ||
| NodeJS app worker #1 |<------------------->| | | ||
+----------------------------+ | Sloki | | ||
| | | ||
+----------------------------+ TCP / Binary | +-------------------------+ | | ||
| NodeJS app worker #2 |<------------------->| | | | | ||
+----------------------------+ | | | | | ||
| | LokiJS | | | ||
+----------------------------+ TCP / JSONRPC | | fast in-memory database | | | ||
| go/php/python/C/whatever |<------------------->| | | | | ||
+----------------------------+ | | | | | ||
| | | | | ||
+----------------------------+ TCP / Binary | +-------------------------+ | | ||
| sloki-cli |<------------------->| | | ||
+----------------------------+ +-----------------------------------+ | ||
``` | ||
JSONRPC (jayson) | ||
TCP|TLS|HTTP|HTTPS | ||
+----------------------------+ +----------------------------------+ | ||
| | | sloki | | ||
| NodeJS Daemon |<----------------------->| (Local or Remote) | | ||
| | | | | ||
+----------------------------+ | +------------------------+ | | ||
| | | | | ||
+----------------------------+ | | | | | ||
| | | | | | | ||
| NodeJS Daemon |<----------------------->| | LokiJS | | | ||
| | | | (database) | | | ||
+----------------------------+ | | | | | ||
| | | | | ||
+----------------------------+ | +------------------------+ | | ||
| | | | | ||
| CLI |<----------------------->| | | ||
| | | | | ||
+----------------------------+ +----------------------------------+ | ||
## 1.i. Transports | ||
For moment, only TCP transport is supported. The advantage of TCP vs HTTP API is that the connection is persistent. | ||
By default, Sloki listens on the following ports: | ||
| Port | Transport | TLS | Protocol | ops/sec/client | ||
|:---------:|------------|------|------------------|------------ | ||
| 6370 | TCP | NO | Binary (fastest) | avg 18K ops/sec | ||
| 6371 | TCP | YES | Binary (fastest) | avg 25K ops/sec (??) | ||
| 6372 | TCP | NO | JSONRPC | avg 17K ops/sec | ||
| 6373 | TCP | YES | JSONRPC | avg 24K ops/sec (??) | ||
If somebody have an idea why TLS is fastest than TCP, i'd like to know .. :) | ||
You will need a [client](#clients) to speak with sloki. | ||
## 1.ii. Protocols | ||
### 1.ii.a. Binary protocol (default) | ||
The binary protocol has been made with performance in mind. Payloads looks like JSONRPC, but it's not. | ||
``` | ||
REQUEST | RESPONSE | ||
------------------------------------------- | -------------------------------------- | ||
{ | { | ||
"m":"myMethod", | "r":true, | ||
"p":["foo","bar"], | "id":"operation-uniq-id" | ||
"id":"operation-uniq-id" | } | ||
} | | ||
``` | ||
* Payload is a little lighter compared to compliant JSONRPC protocol described below (i.e no `jsonrpc` version attribute, `method` become `m`, `params` become `p`, `result` become `r`) | ||
* [Missive](https://github.com/StarryInternet/missive) package is used both server and client side to transform payloads into a binary format. Missive support zlib compression, but it's not used here and it's not recommended because of performance crumble. Missive is based on [fringe](https://github.com/StarryInternet/fringe), an extensible message framing over streams for nodejs. | ||
### 1.ii.b. **JSONRPC** | ||
The JSONRPC protocol has been chosen for interoperability. | ||
``` | ||
REQUEST | RESPONSE | ||
------------------------------------------- | -------------------------------------- | ||
{ | { | ||
"jsonrpc":"2.0", | "jsonrpc":"2.0" | ||
"method":"myMethod", | "result":true, | ||
"params":["foo","bar"], | "id":"operation-uniq-id" | ||
"id":"operation-uniq-id" | } | ||
} | | ||
``` | ||
* Raw and standard JSONRPC over TCP | ||
* [jayson](https://github.com/tedeh/jayson) package is used server side. Actually only TCP transport is implemented, but HTTP(s) JSON API and websocket API may be implemented in the future. | ||
----- | ||
## Installation | ||
## Server Installation | ||
* ```npm install -g sloki``` | ||
## Usage | ||
## Server Usage | ||
@@ -61,2 +113,6 @@ * `sloki` | ||
The client will load every methods that sloki server have, so, the client documentation is not really usefull | ||
## Benchmarks | ||
@@ -100,11 +156,14 @@ | ||
| Status | Command | Parameter | Description | ||
| Status | Method | Parameter | Description | ||
|:-----------------:|-------------------|---------------|---------------- | ||
| :heavy_check_mark:| quit | | disconnect (TCP/TLS clients only) | ||
| :heavy_check_mark:| shutdown | | shutdown sloki | ||
| :heavy_check_mark:| memory | | return sloki memory usage | ||
| :heavy_check_mark:| clients | | return TCP/TLS connected clients | ||
| :heavy_check_mark:| gc | | invoke gc(), for testing purpose | ||
| :heavy_check_mark:| maxClients | | return TCP/TLS maxClients | ||
| :heavy_check_mark:| maxClients | maxClients | set TCP/TLS maxClients | ||
| :heavy_check_mark:| commands | | return available commands | ||
| :heavy_check_mark:| memory | | return sloki memory usage | ||
| :heavy_check_mark:| methods | | return sloki methods | ||
| :heavy_check_mark:| quit | | disconnect (TCP/TLS clients only) | ||
| :heavy_check_mark:| shutdown | | shutdown sloki | ||
| :heavy_check_mark:| version | | return versions (sloki, lokijs, sloki-node-client) | ||
| :heavy_check_mark:| wait | | wait for one second, for testing purpose | ||
@@ -165,7 +224,7 @@ </p> | ||
|:-----------------:|-------------------------------|-----------------------------------|---------------- | ||
| :heavy_check_mark:| find | collectionName, filter | find document(s) | ||
| :heavy_check_mark:| get | collectionName, lokiId | return a document by his id | ||
| :heavy_check_mark:| insert | collectionName, document | insert a document | ||
| :heavy_check_mark:| insert | collectionName, document | insert one or more document(s) | ||
| :heavy_check_mark:| remove | collectionName, document or id | remove one or more document(s) | ||
| :heavy_check_mark:| update | collectionName, document | update a document | ||
| :heavy_check_mark:| remove | collectionName, document or id | remove a document | ||
| :heavy_check_mark:| find | collectionName, filter | find document(s) | ||
@@ -221,2 +280,70 @@ </p> | ||
TODO | ||
`$ sloki --help` | ||
``` | ||
======================================================================= | ||
Sloki - a NodeJS Server for LokyJS | ||
======================================================================= | ||
Environnement variable Default | ||
SLOKI_TCP_BINARY_ENABLE true | ||
SLOKI_TCP_BINARY_PORT 6370 | ||
SLOKI_TCP_BINARY_HOST localhost | ||
SLOKI_TCP_BINARY_MAX_CLIENTS 64 | ||
SLOKI_TLS_BINARY_ENABLE true | ||
SLOKI_TLS_BINARY_PORT 6371 | ||
SLOKI_TLS_BINARY_HOST localhost | ||
SLOKI_TLS_BINARY_MAX_CLIENTS 64 | ||
SLOKI_TCP_JSONRPC_ENABLE true | ||
SLOKI_TCP_JSONRPC_PORT 6372 | ||
SLOKI_TCP_JSONRPC_HOST localhost | ||
SLOKI_TCP_JSONRPC_MAX_CLIENTS 64 | ||
SLOKI_TLS_JSONRPC_ENABLE true | ||
SLOKI_TLS_JSONRPC_PORT 6373 | ||
SLOKI_TLS_JSONRPC_HOST localhost | ||
SLOKI_TLS_JSONRPC_MAX_CLIENTS 64 | ||
SLOKI_DIR /home/franck/.sloki | ||
SLOKI_SHOW_OPS_INTERVAL 0 | ||
SLOKI_GC_INTERVAL 3600000 | ||
SLOKI_MEM_LIMIT 26094 Mb | ||
----------------------------------------------------------------------- | ||
Command Line Options Default | ||
--tcp-binary-enable true | ||
--tcp-binary-port 6370 | ||
--tcp-binary-host localhost | ||
--tcp-binary-max-clients 64 | ||
--tls-binary-enable true | ||
--tls-binary-port 6371 | ||
--tls-binary-host localhost | ||
--tls-binary-max-clients 64 | ||
--tcp-jsonrpc-enable true | ||
--tcp-jsonrpc-port 6372 | ||
--tcp-jsonrpc-host localhost | ||
--tcp-jsonrpc-max-clients 64 | ||
--tls-jsonrpc-enable true | ||
--tls-jsonrpc-port 6373 | ||
--tls-jsonrpc-host localhost | ||
--tls-jsonrpc-max-clients 64 | ||
--dir /home/franck/.sloki | ||
--show-ops-interval 0 | ||
--gc-interval 3600000 | ||
--mem-limit 26094 Mb | ||
----------------------------------------------------------------------- | ||
Examples: | ||
$ sloki # will use defaults | ||
$ sloki --tcp-binary-port=6370 --tcp-binary-host=localhost | ||
``` | ||
The default values can be overridden first with those of the environment variables, | ||
and then those of the command line options. | ||
* SLOKI_DIR `/path/to/sloki` | ||
* default is user's home (~user/.sloki) | ||
* subdirectory `dbs` contains lokijs databases | ||
* subdirectory `certs` contains SSL Certificates for TLS | ||
* directories will be created if not exist | ||
* SLOKI_MEM_LIMIT | ||
* by default, 80% of the available memory | ||
* SLOKI_GC_INTERVAL | ||
* run nodejs garbage collector at regular interval (value in milliseconds) |
const argv = require('minimist')(process.argv.slice(2)); | ||
const use = require('abrequire'); | ||
const Client = use('src/Client'); | ||
const readline = require('readline'); | ||
const run = require('./run'); | ||
let Client; | ||
if (process.env.NODE_ENV === 'dev') { | ||
Client = require('../../../clients/node'); | ||
process.on('uncaughtException', function (err) { | ||
console.log('uncaughtException'); | ||
console.log(err); | ||
}); | ||
process.on('unhandledRejection', (reason, p) => { | ||
console.log('unhandledRejection reason'); | ||
console.log(reason); | ||
console.log('promise'); | ||
console.log(p); | ||
console.log('stack'); | ||
console.log(reason.stack); | ||
}); | ||
} else { | ||
Client = require('sloki-node-client'); | ||
} | ||
if (!argv._[0]) { | ||
@@ -11,41 +32,15 @@ require('./usage'); | ||
const client = new Client(argv._[0]); | ||
function onConnected() { | ||
console.log('connected'); | ||
const completions = client.commandsName(); | ||
(async function() { | ||
function autoComplete(line) { | ||
const hits = completions.filter((c) => c.startsWith(line)); | ||
return [hits.length ? hits : completions, line]; | ||
} | ||
const client = new Client(argv._[0]); | ||
const rl = readline.createInterface({ | ||
input: process.stdin, | ||
output: process.stdout, | ||
completer:autoComplete | ||
}); | ||
rl.setPrompt('> '); | ||
rl.prompt(); | ||
rl.on('line', (data) => { | ||
if (data === 'quit') { | ||
rl.close(); | ||
client.quit(); | ||
await client.connect((err) => { | ||
if (err) { | ||
console.log(err.message); | ||
return; | ||
} | ||
rl.prompt(); | ||
run(argv._[0], client); | ||
}); | ||
} | ||
function onError(err) { | ||
throw Error(err); | ||
} | ||
client.connect().then(onConnected); | ||
client.on('close', () => { | ||
console.log('close'); | ||
}); | ||
client.on('error', onError); | ||
})(); |
@@ -1,8 +0,16 @@ | ||
const use = require('abrequire'); | ||
const ENV = use('src/env'); | ||
const config = require('../config'); | ||
console.log('Usage: sloki-cli <url>'); | ||
console.log(); | ||
console.log('Examples:'); | ||
console.log(' sloki-cli tcp://localhost:'+ENV.NET_TCP_PORT); | ||
console.log(' sloki-cli tls://localhost:'+ENV.NET_TCP_PORT); | ||
console.log('Examples with ports:'); | ||
console.log(`sloki-cli tcp://localhost:${config.TCP_BINARY_PORT}`); | ||
console.log(`sloki-cli tls://localhost:${config.TLS_BINARY_PORT}`); | ||
console.log(`sloki-cli binary://localhost:${config.TCP_BINARY_PORT}`); | ||
console.log(`sloki-cli binarys://localhost:${config.TLS_BINARY_PORT}`); | ||
console.log(`sloki-cli jsonrpc://localhost:${config.TCP_JSONRPC_PORT}`); | ||
console.log(`sloki-cli jsonrpcs://localhost:${config.TLS_JSONRPC_PORT}`); | ||
console.log(''); | ||
console.log('Notes:'); | ||
console.log(' * tcp protocol is an alias for binary protocol'); | ||
console.log(' * tls protocol is an alias for binarys protocol'); | ||
console.log(' * you may not specify the port, in which case the default ports will be used.'); |
@@ -7,3 +7,3 @@ const log = require('evillogger')({ ns:'loki' }); | ||
const ENV = require('./env'); | ||
const config = require('./config'); | ||
const shared = require('./methods/shared'); | ||
@@ -13,5 +13,5 @@ | ||
if (!fs.pathExistsSync(ENV.DATABASES_DIRECTORY)) { | ||
fs.ensureDirSync(ENV.DATABASES_DIRECTORY); | ||
log.info('Directory %s created', ENV.DATABASES_DIRECTORY); | ||
if (!fs.pathExistsSync(config.SLOKI_DIR_DBS)) { | ||
fs.ensureDirSync(config.SLOKI_DIR_DBS); | ||
log.info(`directory ${config.SLOKI_DIR_DBS} created`); | ||
} | ||
@@ -22,9 +22,9 @@ | ||
for (file of klawSync(ENV.DATABASES_DIRECTORY)) { | ||
for (file of klawSync(config.SLOKI_DIR_DBS)) { | ||
dbName = path.basename(file.path).replace(/\.json/, ''); | ||
log.info(`Loading database ${file.path}`); | ||
log.info(`loading database ${file.path}`); | ||
shared.dbs[dbName] = new loki(file.path, shared.DEFAULT_DATABASE_OPTIONS); | ||
} | ||
const dbTestFile = path.resolve(ENV.DATABASES_DIRECTORY+'/test.json'); | ||
const dbTestFile = path.resolve(config.SLOKI_DIR_DBS+'/test.json'); | ||
@@ -31,0 +31,0 @@ if (!shared.dbs['test']) { |
@@ -6,27 +6,20 @@ const log = require('evillogger')({ ns:'collection/find' }); | ||
const descriptor = { | ||
name:'find', | ||
categories:['collection'], | ||
description:{ | ||
short:'Find a document' | ||
}, | ||
parameters:[ | ||
{ | ||
name:'Collection name', | ||
mandatory:true, | ||
title:'find', | ||
description:'Find a document', | ||
type: 'object', | ||
properties:{ | ||
'collection':{ | ||
alias:['col', 'c'], | ||
description:'Collection name', | ||
sanityCheck:{ | ||
type:'string', | ||
reString:shared.RE_COLLETION_NAME, | ||
reFlag:'i' | ||
} | ||
type:'string', | ||
pattern:shared.RE_COLLETION_NAME, | ||
patternFlag:'i' | ||
}, | ||
{ | ||
name:'Filters', | ||
mandatory:false, | ||
'filters':{ | ||
alias:['f'], | ||
description:'Filters', | ||
sanityCheck:{ | ||
type:'object' | ||
} | ||
type:'object' | ||
} | ||
] | ||
}, | ||
required:['collection'] | ||
}; | ||
@@ -45,6 +38,6 @@ | ||
*/ | ||
function handler(params, callback, socket) { | ||
const databaseName = socket.loki.currentDatabase; | ||
const collectionName = params[0]; | ||
const filters = params[1]; | ||
function handler(params, context, callback) { | ||
const databaseName = context.session.loki.currentDatabase; | ||
const collectionName = params.collection; | ||
const filters = params.filters; | ||
@@ -51,0 +44,0 @@ if (!shared.collectionExists(databaseName, collectionName, callback)) { |
@@ -5,27 +5,19 @@ const shared = require('../../shared'); | ||
const descriptor = { | ||
name:'get', | ||
categories:['collection'], | ||
description:{ | ||
short:'Get a document by id' | ||
}, | ||
parameters:[ | ||
{ | ||
name:'Collection name', | ||
mandatory:true, | ||
title:'get', | ||
description:'Get a document by id', | ||
type: 'object', | ||
properties:{ | ||
'collection':{ | ||
alias:['col', 'c'], | ||
description:'Collection name', | ||
sanityCheck:{ | ||
type:'string', | ||
reString:shared.RE_COLLETION_NAME, | ||
reFlag:'i' | ||
} | ||
type:'string', | ||
pattern:shared.RE_COLLETION_NAME, | ||
patternFlag:'i' | ||
}, | ||
{ | ||
name:'Unique ID', | ||
mandatory:true, | ||
'id':{ | ||
description:'Loki id', | ||
sanityCheck:{ | ||
type:'number' | ||
} | ||
type:'number' | ||
} | ||
] | ||
}, | ||
required:['collection', 'id'] | ||
}; | ||
@@ -44,6 +36,6 @@ | ||
*/ | ||
function handler(params, callback, socket) { | ||
const databaseName = socket.loki.currentDatabase; | ||
const collectionName = params[0]; | ||
const lokiId = params[1]; | ||
function handler(params, context, callback) { | ||
const databaseName = context.session.loki.currentDatabase; | ||
const collectionName = params.collection; | ||
const lokiId = params.id; | ||
@@ -50,0 +42,0 @@ if (!shared.collectionExists(databaseName, collectionName, callback)) { |
@@ -6,35 +6,25 @@ const log = require('evillogger')({ ns:'collection/insert' }); | ||
const descriptor = { | ||
name:'insert', | ||
categories:['collection'], | ||
description:{ | ||
short:'Add a document' | ||
}, | ||
parameters:[ | ||
{ | ||
name:'Collection name', | ||
mandatory:true, | ||
title:'insert', | ||
description:'Insert a document', | ||
type: 'object', | ||
properties:{ | ||
'collection':{ | ||
alias:['col', 'c'], | ||
description:'Collection name', | ||
sanityCheck:{ | ||
type:'string', | ||
reString:shared.RE_COLLETION_NAME, | ||
reFlag:'i' | ||
} | ||
type:'string', | ||
pattern:shared.RE_COLLETION_NAME, | ||
patternFlag:'i' | ||
}, | ||
{ | ||
name:'Document', | ||
mandatory:true, | ||
'document':{ | ||
alias:['doc', 'd'], | ||
description:'Document', | ||
sanityCheck:{ | ||
type:'object' | ||
} | ||
type:'object' | ||
}, | ||
{ | ||
name:'Options', | ||
mandatory:false, | ||
'options':{ | ||
alias:['opts', 'o'], | ||
description:'Insert options', | ||
sanityCheck:{ | ||
type:'object' | ||
} | ||
type:'object' | ||
} | ||
] | ||
}, | ||
required:['collection', 'document'] | ||
}; | ||
@@ -53,7 +43,7 @@ | ||
*/ | ||
function handler(params, callback, socket) { | ||
const databaseName = socket.loki.currentDatabase; | ||
const collectionName = params[0]; | ||
const doc = params[1]; | ||
const options = params[2]; | ||
function handler(params, context, callback) { | ||
const databaseName = context.session.loki.currentDatabase; | ||
const collectionName = params.collection; | ||
const doc = params.document; | ||
const options = params.options; | ||
@@ -60,0 +50,0 @@ if (!shared.databaseSelected(databaseName, callback)) { |
@@ -6,25 +6,29 @@ const log = require('evillogger')({ ns:'collection/remove' }); | ||
const descriptor = { | ||
name:'remove', | ||
categories:['collection'], | ||
description:{ | ||
short:'Remove a document by id' | ||
title:'remove', | ||
description:'Remove a document by id', | ||
type: 'object', | ||
properties:{ | ||
'collection':{ | ||
alias:['col', 'c'], | ||
description:'Collection name', | ||
type:'string', | ||
pattern:shared.RE_COLLETION_NAME, | ||
patternFlag:'i' | ||
}, | ||
'document':{ | ||
alias:['doc', 'd'], | ||
description:'Document', | ||
type:'object' | ||
}, | ||
'id':{ | ||
description:'Document ID', | ||
type:'number' | ||
} | ||
}, | ||
parameters:[ | ||
oneOf:[ | ||
{ | ||
name:'Collection name', | ||
mandatory:true, | ||
description:'Collection name', | ||
sanityCheck:{ | ||
type:'string', | ||
reString:shared.RE_COLLETION_NAME, | ||
reFlag:'i' | ||
} | ||
required:['collection', 'document'] | ||
}, | ||
{ | ||
name:'document or document id', | ||
mandatory:true, | ||
description:'Document or document ID', | ||
sanityCheck:{ | ||
type:['number', 'object'] | ||
} | ||
required:['collection', 'id'] | ||
} | ||
@@ -45,6 +49,6 @@ ] | ||
*/ | ||
function handler(params, callback, socket) { | ||
const databaseName = socket.loki.currentDatabase; | ||
const collectionName = params[0]; | ||
const documentOrId = params[1]; | ||
function handler(params, context, callback) { | ||
const databaseName = context.session.loki.currentDatabase; | ||
const collectionName = params.collection; | ||
const documentOrId = params.document || params.id; | ||
@@ -51,0 +55,0 @@ if (!shared.collectionExists(databaseName, collectionName, callback)) { |
@@ -6,27 +6,20 @@ const log = require('evillogger')({ ns:'collection/remove' }); | ||
const descriptor = { | ||
name:'update', | ||
categories:['collection'], | ||
description:{ | ||
short:'Update a document' | ||
}, | ||
parameters:[ | ||
{ | ||
name:'Collection name', | ||
mandatory:true, | ||
title:'update', | ||
description:'Update a document', | ||
type: 'object', | ||
properties:{ | ||
'collection':{ | ||
alias:['col', 'c'], | ||
description:'Collection name', | ||
sanityCheck:{ | ||
type:'string', | ||
reString:shared.RE_COLLETION_NAME, | ||
reFlag:'i' | ||
} | ||
type:'string', | ||
pattern:shared.RE_COLLETION_NAME, | ||
patternFlag:'i' | ||
}, | ||
{ | ||
name:'document', | ||
mandatory:true, | ||
'document':{ | ||
alias:['doc', 'd'], | ||
description:'Document', | ||
sanityCheck:{ | ||
type:['object'] | ||
} | ||
type:'object' | ||
} | ||
] | ||
}, | ||
required:['collection', 'document'] | ||
}; | ||
@@ -45,6 +38,6 @@ | ||
*/ | ||
function handler(params, callback, socket) { | ||
const databaseName = socket.loki.currentDatabase; | ||
const collectionName = params[0]; | ||
const doc = params[1]; | ||
function handler(params, context, callback) { | ||
const databaseName = context.session.loki.currentDatabase; | ||
const collectionName = params.collection; | ||
const doc = params.document; | ||
@@ -51,0 +44,0 @@ if (!shared.collectionExists(databaseName, collectionName, callback)) { |
@@ -5,27 +5,19 @@ const shared = require('../../shared'); | ||
const descriptor = { | ||
name:'addCollection', | ||
categories:['database'], | ||
description:{ | ||
short:'Add a collection into currently selected database' | ||
}, | ||
parameters:[ | ||
{ | ||
name:'Collection name', | ||
mandatory:true, | ||
title:'addCollection', | ||
description:'Add a collection into currently selected database', | ||
type:'object', | ||
properties:{ | ||
'collection':{ | ||
alias:['col', 'c'], | ||
description:'Collection name', | ||
sanityCheck:{ | ||
type:'string', | ||
reString:shared.RE_COLLETION_NAME, | ||
reFlag:'i' | ||
} | ||
type:'string', | ||
pattern:shared.RE_COLLETION_NAME, | ||
patternFlag:'i' | ||
}, | ||
{ | ||
name:'Options', | ||
mandatory:false, | ||
'options':{ | ||
description:'Collection options', | ||
sanityCheck:{ | ||
type:'object' | ||
} | ||
type:'object' | ||
} | ||
] | ||
}, | ||
required:['collection'] | ||
}; | ||
@@ -44,6 +36,6 @@ | ||
*/ | ||
function handler(params, callback, socket) { | ||
const databaseName = socket.loki.currentDatabase; | ||
const collectionName = params[0]; | ||
const collectionOptions = params[1]; | ||
function handler(params, context, callback) { | ||
const databaseName = context.session.loki.currentDatabase; | ||
const collectionName = params.collection; | ||
const collectionOptions = params.options; | ||
@@ -50,0 +42,0 @@ if (!shared.databaseSelected(databaseName, callback)) { |
const Method = require('../../Method'); | ||
const descriptor = { | ||
name:'db', | ||
categories:['database'], | ||
description:{ | ||
short:'Return currently selected database name', | ||
}, | ||
parameters:[] | ||
title:'db', | ||
description:'Return currently selected database name' | ||
}; | ||
@@ -23,4 +19,4 @@ | ||
*/ | ||
function handler(params, callback, socket) { | ||
callback(null, socket.loki.currentDatabase); | ||
function handler(params, context, callback) { | ||
callback(null, context.session.loki.currentDatabase); | ||
} | ||
@@ -27,0 +23,0 @@ |
@@ -5,19 +5,14 @@ const shared = require('../../shared'); | ||
const descriptor = { | ||
name:'getCollection', | ||
categories:['database'], | ||
description:{ | ||
short:'Return collection properties', | ||
}, | ||
parameters:[ | ||
{ | ||
name:'Collection name', | ||
mandatory:true, | ||
title:'getCollection', | ||
description:'Return collection properties', | ||
properties:{ | ||
'collection':{ | ||
alias:['col', 'c'], | ||
description:'Collection name', | ||
sanityCheck:{ | ||
type:'string', | ||
reString:shared.RE_COLLETION_NAME, | ||
reFlag:'i' | ||
} | ||
type:'string', | ||
pattern:shared.RE_COLLETION_NAME, | ||
patternFlag:'i' | ||
} | ||
] | ||
}, | ||
required:['collection'] | ||
}; | ||
@@ -36,5 +31,5 @@ | ||
*/ | ||
function handler(params, callback, socket) { | ||
const databaseName = socket.loki.currentDatabase; | ||
const collectionName = params[0]; | ||
function handler(params, context, callback) { | ||
const databaseName = context.session.loki.currentDatabase; | ||
const collectionName = params.collection; | ||
@@ -41,0 +36,0 @@ if (!shared.databaseSelected(databaseName, callback)) { |
@@ -5,8 +5,4 @@ const shared = require('../../shared'); | ||
const descriptor = { | ||
name:'listCollections', | ||
categories:['database'], | ||
description:{ | ||
short:'Return collections in currently selected database', | ||
}, | ||
parameters:[] | ||
title:'listCollections', | ||
description:'Return collections in currently selected database' | ||
}; | ||
@@ -25,4 +21,4 @@ | ||
*/ | ||
function handler(params, callback, socket) { | ||
const databaseName = socket.loki.currentDatabase; | ||
function handler(params, context, callback) { | ||
const databaseName = context.session.loki.currentDatabase; | ||
@@ -29,0 +25,0 @@ if (!shared.databaseSelected(databaseName, callback)) { |
@@ -5,8 +5,4 @@ const shared = require('../../shared'); | ||
const descriptor = { | ||
name:'listDatabases', | ||
categories:['database'], | ||
description:{ | ||
short:'Return available databases', | ||
}, | ||
parameters:[] | ||
title:'listDatabases', | ||
description:'Return available databases' | ||
}; | ||
@@ -25,3 +21,3 @@ | ||
*/ | ||
function handler(params, callback) { | ||
function handler(params, context, callback) { | ||
@@ -28,0 +24,0 @@ const dbs = []; |
const log = require('evillogger')({ ns:'database/loadDatabase' }); | ||
const shared = require('../../shared'); | ||
const Method = require('../../Method'); | ||
const path = require('path'); | ||
const loki = require('lokijs'); | ||
const descriptor = { | ||
name:'loadDatabase', | ||
categories:['database'], | ||
description:{ | ||
short:'Select a database (if not exist, a new db will be created)' | ||
}, | ||
parameters:[ | ||
{ | ||
name:'database name', | ||
mandatory:true, | ||
mandatoryError:'Database name is mandatory', | ||
title:'loadDatabase', | ||
description:'Select a database (if not exist, a new db will be created)', | ||
properties:{ | ||
'database':{ | ||
alias:['db', 'd'], | ||
description:'Database name', | ||
sanityCheck:{ | ||
type:'string', | ||
reString:shared.RE_DATABASE_NAME, | ||
reFlag:'i', | ||
} | ||
type:'string', | ||
pattern:shared.RE_DATABASE_NAME, | ||
patternFlag:'i' | ||
}, | ||
{ | ||
name:'Options', | ||
mandatory:false, | ||
'options':{ | ||
alias:['opts', 'o'], | ||
description:'Database options', | ||
sanityCheck:{ | ||
type:'object' | ||
} | ||
type:'object' | ||
} | ||
] | ||
}, | ||
required:['database'] | ||
}; | ||
@@ -47,30 +36,29 @@ | ||
*/ | ||
function handler(params, callback, socket) { | ||
const databaseName = params[0]; | ||
const databaseOptions = params[1]||{}; | ||
function handler(params, context, callback) { | ||
const databaseName = params.database; | ||
const databaseOptions = params.options; | ||
if (shared.dbs[databaseName]) { | ||
socket.loki.currentDatabase = databaseName; | ||
log.info(`${socket.id}: current database is now ${databaseName} (loaded)`); | ||
callback(null, shared.dbs[databaseName]); | ||
return; | ||
function ret(result, created) { | ||
context.session.loki.currentDatabase = databaseName; | ||
if (created) { | ||
log.info(`${context.session.id}: current database is now ${databaseName} (created)`); | ||
} else { | ||
log.info(`${context.session.id}: current database is now ${databaseName}`); | ||
} | ||
callback(null, result); | ||
} | ||
const dbPath = path.resolve(shared.ENV.DATABASES_DIRECTORY+`/${databaseName}.json`); | ||
const options = Object.assign(shared.DEFAULT_DATABASE_OPTIONS, databaseOptions||{}); | ||
shared.getDatabase(databaseName, (err, result) => { | ||
if (result) { | ||
ret(result); | ||
return; | ||
} | ||
options.autoloadCallback = () => { | ||
socket.loki.currentDatabase = databaseName; | ||
log.info(`${socket.id}: current database is now ${databaseName} (created)`); | ||
callback(null, shared.dbs[databaseName]); | ||
}; | ||
shared.createDatabase(databaseName, databaseOptions, (err, result) => { | ||
ret(result, true); | ||
}); | ||
}); | ||
//log.info(`${socket.id}: creating database ${databaseName}`); | ||
shared.dbs[databaseName] = new loki(dbPath, options); | ||
// by default, immediate save (force flush) after creating database | ||
shared.ENV.DATABASES_FORCE_SAVE_ON_CREATE && shared.dbs[databaseName].save(); | ||
} | ||
module.exports = new Method(descriptor, handler); |
@@ -5,8 +5,4 @@ const shared = require('../../shared'); | ||
const descriptor = { | ||
name:'saveDatabase', | ||
categories:['database'], | ||
description:{ | ||
short:'Force save of currently selected database', | ||
}, | ||
parameters:[] | ||
title:'saveDatabase', | ||
description:'Force save of currently selected database' | ||
}; | ||
@@ -26,4 +22,4 @@ | ||
*/ | ||
function handler(params, callback, socket) { | ||
const databaseName = socket.loki.currentDatabase; | ||
function handler(params, context, callback) { | ||
const databaseName = context.session.loki.currentDatabase; | ||
@@ -34,3 +30,3 @@ if (!shared.databaseSelected(databaseName, callback)) { | ||
shared.dbs[databaseName].saveDatabase((err) => { | ||
shared.dbs[databaseName].saveDatabase(err => { | ||
if (err) { | ||
@@ -37,0 +33,0 @@ callback({ |
const Method = require('../../Method'); | ||
const descriptor = { | ||
name:'clients', | ||
categories:['server'], | ||
description:{ | ||
short:'Return connected clients list' | ||
}, | ||
parameters:[] | ||
title:'clients', | ||
description:'Return connected clients list' | ||
}; | ||
@@ -23,6 +19,6 @@ | ||
*/ | ||
function handler(params, callback, socket) { | ||
callback(null, Object.keys(socket.server.clients)); | ||
function handler(params, context, callback) { | ||
callback(null, Object.keys(context.server.clients)); | ||
} | ||
module.exports = new Method(descriptor, handler); |
@@ -1,37 +0,30 @@ | ||
const shared = require('../../shared'); | ||
const log = require('evillogger')({ ns:'server/maxClients' }); | ||
const Method = require('../../Method'); | ||
const descriptor = { | ||
name:'maxClients', | ||
categories:['server'], | ||
description:{ | ||
short:'Return or set maximum number of allowed simultaneous connected clients (TCP/TLS)', | ||
}, | ||
parameters:[ | ||
{ | ||
name:'value', | ||
mandatory:false, | ||
title:'maxClients', | ||
description:'Return or set maximum number of allowed simultaneous connected clients (TCP/TLS)', | ||
properties:{ | ||
'value':{ | ||
description:'Maximum number of allowed simultaneous connected clients (between 1 and 1000)', | ||
sanityCheck:{ | ||
type:'number', | ||
reString:'^([1-9][0-9]{0,2}|1000)$', | ||
reFlag:'', | ||
reError:'maxClients should be a number between 1 and 1000' | ||
} | ||
type:'number', | ||
minimum: 1, | ||
maximum: 1000, | ||
} | ||
] | ||
} | ||
}; | ||
function handler(params, callback) { | ||
if (!params) { | ||
callback(null, shared.ENV.NET_TCP_MAX_CLIENTS); | ||
function handler(params, context, callback) { | ||
if (!params.value) { | ||
callback(null, context.server.getMaxClients()); | ||
return; | ||
} | ||
const maxClient = params[0]; | ||
const maxClients = params.value; | ||
shared.ENV.NET_TCP_MAX_CLIENTS = parseInt(maxClient); | ||
callback(null, shared.ENV.NET_TCP_MAX_CLIENTS); | ||
context.server.setMaxClients(maxClients); | ||
log.info(`${context.session.id}: maxClients has been set to ${maxClients} for protocol ${context.server.protocol}`); | ||
callback(null, maxClients); | ||
} | ||
module.exports = new Method(descriptor, handler); |
const Method = require('../../Method'); | ||
const prettyBytes = require('pretty-bytes'); | ||
const os = require('os'); | ||
const descriptor = { | ||
name:'memory', | ||
categories:['server'], | ||
description:{ | ||
short:'Return sloki process memory usage', | ||
}, | ||
parameters:[] | ||
title:'memory', | ||
description:'Return sloki process memory usage' | ||
}; | ||
@@ -24,3 +21,3 @@ | ||
*/ | ||
function handler(params, callback) { | ||
function handler(params, context, callback) { | ||
const used = process.memoryUsage(); | ||
@@ -31,5 +28,14 @@ for (const key in used) { | ||
callback(null, used); | ||
const osmem = { | ||
free:os.freemem(), | ||
total:os.totalmem() | ||
}; | ||
for (const key in osmem) { | ||
osmem[key] = prettyBytes(osmem[key]); | ||
} | ||
callback(null, { process:used, os:osmem }); | ||
} | ||
module.exports = new Method(descriptor, handler); |
const Method = require('../../Method'); | ||
const descriptor = { | ||
name:'methods', | ||
categories:['server'], | ||
description:{ | ||
short:'Return methods list', | ||
}, | ||
parameters:[] | ||
title:'methods', | ||
description:'Return methods list' | ||
}; | ||
@@ -24,3 +20,3 @@ | ||
*/ | ||
function handler(params, callback) { | ||
function handler(params, context, callback) { | ||
callback(null, require('../../').listWithDescriptor()); | ||
@@ -27,0 +23,0 @@ } |
const Method = require('../../Method'); | ||
const descriptor = { | ||
name:'quit', | ||
categories:['server'], | ||
description:{ | ||
short:'Disconnect', | ||
}, | ||
parameters:[] | ||
title:'quit', | ||
description:'Disconnect' | ||
}; | ||
@@ -24,7 +19,7 @@ | ||
*/ | ||
function handler(params, callback, socket) { | ||
function handler(params, context, callback) { | ||
callback(null, 'bye'); | ||
socket.end(); | ||
context.session.end(); | ||
} | ||
module.exports = new Method(descriptor, handler); |
@@ -5,8 +5,4 @@ const Method = require('../../Method'); | ||
const descriptor = { | ||
name:'shutdown', | ||
categories:['server'], | ||
description:{ | ||
short:'Shutdown sloki server', | ||
}, | ||
parameters:[] | ||
title:'shutdown', | ||
description:'Shutdown sloki server' | ||
}; | ||
@@ -24,3 +20,3 @@ | ||
*/ | ||
function handler(params, callback) { | ||
function handler(params, context, callback) { | ||
require('../../../server').stop(); | ||
@@ -27,0 +23,0 @@ callback(); |
const Method = require('../../Method'); | ||
const descriptor = { | ||
name:'wait', | ||
categories:['server'], | ||
description:{ | ||
short:'Wait for one sec' | ||
}, | ||
parameters:[] | ||
title:'wait', | ||
description:'Wait for one sec' | ||
}; | ||
@@ -22,3 +18,3 @@ | ||
*/ | ||
function handler(params, callback) { | ||
function handler(params, context, callback) { | ||
setTimeout(() => { | ||
@@ -25,0 +21,0 @@ callback(); |
@@ -5,4 +5,4 @@ const log = require('evillogger')({ ns:'methods' }); | ||
const commands = {}; | ||
const commandsDescriptor = {}; | ||
const methods = {}; | ||
const methodsDescriptor = {}; | ||
const reDirname = new RegExp(__dirname+'/'); | ||
@@ -26,17 +26,16 @@ const showLog = !process.mainModule.filename.match(/\/cli/); | ||
} | ||
showLog && log.debug(`Command registered ${cmdBase}/${cmdName}`); | ||
commands[cmdName] = require(file.path); | ||
commandsDescriptor[cmdName] = commands[cmdName].getDescriptor(); | ||
showLog && log.debug(`method registered ${cmdBase}/${cmdName}`); | ||
methods[cmdName] = require(file.path); | ||
methodsDescriptor[cmdName] = methods[cmdName].getDescriptor(); | ||
} | ||
function getHandler(command, params, scope) { | ||
if (!commands[command]) { | ||
function getHandler(method, params, context) { | ||
if (!methods[method]) { | ||
return; | ||
} | ||
// @TODO: optimize scope ? | ||
if (scope) { | ||
return commands[command].handle.bind(scope); | ||
if (context) { | ||
return methods[method].handle.bind(context); | ||
} else { | ||
return commands[command].handle; | ||
return methods[method].handle; | ||
} | ||
@@ -46,12 +45,12 @@ } | ||
function list() { | ||
return commands; | ||
return methods; | ||
} | ||
function listWithDescriptor() { | ||
return commandsDescriptor; | ||
return methodsDescriptor; | ||
} | ||
function exists(command) { | ||
if (commands[command]) { | ||
function exists(method) { | ||
if (methods[method]) { | ||
return true; | ||
@@ -62,2 +61,10 @@ } | ||
function exec(method, params, context, callback) { | ||
try { | ||
methods[method].handle(params, context, callback); | ||
} catch(err) { | ||
callback(err); | ||
} | ||
} | ||
module.exports = { | ||
@@ -67,3 +74,4 @@ list, | ||
exists, | ||
getHandler | ||
getHandler, | ||
exec | ||
}; |
@@ -1,93 +0,193 @@ | ||
const log = require('evillogger')({ ns:'Command' }); | ||
const log = require('evillogger')({ ns:'Method' }); | ||
const shared = require('./shared'); | ||
const config = require('../config'); | ||
// http://jsonrpc.org/spec.html#error_object | ||
const ERROR_CODE_PARAMETER = -32602; | ||
function triggerError(msg, callback) { | ||
callback({ code:ERROR_CODE_PARAMETER, message:msg }); | ||
log.warn(msg); | ||
callback({ code:shared.ERROR_CODE_PARAMETER, message:msg }); | ||
if (config.TCP_ENGINE!='binary') { | ||
log.warn(msg); | ||
} | ||
} | ||
function triggerErrorInternal(msg, callback) { | ||
callback({ code:shared.ERROR_CODE_INTERNAL, message:msg }); | ||
if (config.TCP_ENGINE!='binary') { | ||
log.warn(msg); | ||
} | ||
} | ||
function Command(descriptor, handler) { | ||
let mandatoryParametersCount = 0; | ||
let descriptorPropertiesCount = 0; | ||
if (descriptor.properties) { | ||
descriptorPropertiesCount = Object.keys(descriptor.properties).length; | ||
} | ||
function handle(params, callback) { | ||
if (!descriptor.required) { | ||
descriptor.required = []; | ||
} | ||
if (!descriptor.oneOf) { | ||
descriptor.oneOf = []; | ||
} | ||
// | ||
// Sanity Checks | ||
// | ||
function unwantedProperties(params) { | ||
if ( | ||
params | ||
&& Object.keys(params).length>0 | ||
&& (!descriptor.properties || !Object.keys(descriptor.properties).length) | ||
) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
if (!params || !params.length) { | ||
if (mandatoryParametersCount) { | ||
triggerError(`${descriptor.name}: number of parameters should be at least ${mandatoryParametersCount}`, callback); | ||
function handle(params, context, callback) { | ||
if (!callback) { | ||
callback = context; | ||
context = this; | ||
} | ||
if (!params) params = {}; | ||
if (descriptor.title.match(/insert|update/)) { | ||
if (config.MEM_LIMIT_REACHED) { | ||
triggerErrorInternal(`method "${descriptor.title}": memory limit reached`, callback); | ||
return; | ||
} | ||
} | ||
// "this" is the socket socket if client is TCP/TLS | ||
handler(params, callback, this); | ||
// request has parameters, but the descriptor don't have | ||
if (unwantedProperties(params)) { | ||
triggerError(`method "${descriptor.title}" does NOT wait for any property`, callback); | ||
return; | ||
} | ||
if (params.length<mandatoryParametersCount) { | ||
triggerError(`${descriptor.name}: number of parameters should be at least ${mandatoryParametersCount}`, callback); | ||
const paramsCount = Object.keys(params).length; | ||
// request has no parameters, as specified in the descriptor | ||
if (descriptorPropertiesCount === 0 && paramsCount === 0) { | ||
handler(params, context, callback); | ||
return; | ||
} | ||
// request has more parameters than expected in the descriptor | ||
if (paramsCount>descriptorPropertiesCount) { | ||
triggerError(`method "${descriptor.title}": too many properties, expected ${descriptorPropertiesCount}, find ${paramsCount}`, callback); | ||
return; | ||
} | ||
if (params.length>descriptor.parameters.length) { | ||
if (descriptor.parameters.length>0) { | ||
triggerError(`${descriptor.name}: number of parameters should be lower or equal than ${descriptor.parameters.length}`, callback); | ||
return; | ||
let property; | ||
for (const prop in descriptor.properties) { | ||
property = descriptor.properties[prop]; | ||
if (property.alias && property.alias.length) { | ||
for (const alias of property.alias) { | ||
if (params[alias] != undefined) { | ||
params[prop] = params[alias]; | ||
delete params[alias]; | ||
} | ||
} | ||
} | ||
triggerError(`${descriptor.name}: this method does not wait for parameters`, callback); | ||
} | ||
let parameter; | ||
for (const prop in descriptor.properties) { | ||
for (let i = 0; i< descriptor.parameters.length; i++) { | ||
property = descriptor.properties[prop]; | ||
parameter = descriptor.parameters[i]; | ||
// a mandatory property is missing | ||
if (descriptor.required.indexOf(prop)>=0 && params[prop] === undefined) { | ||
if (property.alias) { | ||
triggerError(`method "${descriptor.title}": property ${prop} (alias ${property.alias}) is mandatory`, callback); | ||
} else { | ||
triggerError(`method "${descriptor.title}": property ${prop} is mandatory`, callback); | ||
} | ||
return; | ||
} | ||
if (!parameter.sanityCheck) { | ||
// non mandatory property | ||
if (params[prop] === undefined) { | ||
continue; | ||
} | ||
if ( | ||
params[i] != null && params[i] != undefined && | ||
( | ||
(typeof parameter.sanityCheck.type === 'object' && parameter.sanityCheck.type.indexOf(typeof params[i])<0) || | ||
(typeof parameter.sanityCheck.type === 'string' && typeof params[i] != parameter.sanityCheck.type) | ||
) | ||
) { | ||
triggerError(`${descriptor.name}: wrong type for parameter '${parameter.name}' : found '${typeof params[i]}', expected '${parameter.sanityCheck.type}'`, callback); | ||
// check type | ||
if (property.type != typeof params[prop]) { | ||
triggerError(`method "${descriptor.title}": property "${prop}" should be a ${property.type}, found ${typeof params[prop]}`, callback); | ||
return; | ||
} | ||
if (params[i] === null || params[i] === undefined && parameter.mandatory) { | ||
triggerError(`${descriptor.name}: parameter '${parameter.name}' is mandatory`, callback); | ||
return; | ||
} | ||
if (parameter.sanityCheck.re) { | ||
// re is a compiled regexp (see _compileDescriptorParametersRegexp) | ||
if (!parameter.sanityCheck.re.test(params[i])) { | ||
if (parameter.sanityCheck.reError) { | ||
triggerError(`${descriptor.name}: ${parameter.sanityCheck.reError}`, callback); | ||
if (property.type === 'string') { | ||
if (property.pattern) { | ||
// patternRe is the compiled version of the regexp, see _compileDescriptorParametersRegexp | ||
if (!property.patternRe.test(params[prop])) { | ||
triggerError(`method "${descriptor.title}": property "${prop}" does not match regular expression ${property.pattern}`, callback); | ||
return; | ||
} | ||
} | ||
} | ||
triggerError(`${descriptor.name}: parameter '${parameter.name}' does not match regular expression ${parameter.sanityCheck.reString}`, callback); | ||
if (property.type === 'number') { | ||
// greater than | ||
if (typeof property.minimum === 'number' && params[prop]<property.minimum) { | ||
triggerError(`method "${descriptor.title}": property "${prop}" should be > ${property.minimum}`, callback); | ||
return; | ||
} | ||
// lower than | ||
if (typeof property.maximum === 'number' && params[prop]>property.maximum) { | ||
triggerError(`method "${descriptor.title}": property "${prop}" should be < ${property.maximum}`, callback); | ||
return; | ||
} | ||
} | ||
} | ||
if (descriptor.oneOf.length) { | ||
let oneOfMatch = false; | ||
let multipleOneOfFound = false; | ||
let oneOfStr = []; | ||
let strs = []; | ||
for (const oneof of descriptor.oneOf) { | ||
let found = 0; | ||
for (const requiredProperty of oneof.required) { | ||
strs.push(requiredProperty); | ||
if (params[requiredProperty] != undefined) { | ||
found++; | ||
} | ||
} | ||
if (found === descriptor.oneOf.length) { | ||
if (!oneOfMatch) { | ||
oneOfMatch = true; | ||
} else { | ||
multipleOneOfFound = true; | ||
} | ||
} | ||
oneOfStr.push(strs.join(', ')); | ||
strs = []; | ||
} | ||
oneOfStr = oneOfStr.join('" OR "'); | ||
if (multipleOneOfFound) { | ||
triggerError(`method "${descriptor.title}": mandatory properties conflict, please specify properties "${oneOfStr}"`, callback); | ||
return; | ||
} | ||
if (!oneOfMatch) { | ||
triggerError(`method "${descriptor.title}": mandatory properties are missing (at least "${oneOfStr}")`, callback); | ||
return; | ||
} | ||
} | ||
// | ||
// Sanity Check passed successfully | ||
// | ||
// "this" is the socket socket if client is TCP/TLS | ||
handler(params, callback, this); | ||
if (params.nr) { | ||
// if "nr" (like "no response") passed in params, override callback with an empty one | ||
callback = () => {}; | ||
} | ||
handler(params, context, callback); | ||
} | ||
@@ -99,40 +199,15 @@ | ||
function _hasParametersInDescriptor() { | ||
if ( | ||
!descriptor | ||
|| | ||
!descriptor.parameters | ||
|| | ||
!descriptor.parameters.length | ||
) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
function _compileDescriptorParametersRegexp() { | ||
if (!_hasParametersInDescriptor()) { | ||
if (!descriptorPropertiesCount) { | ||
return; | ||
} | ||
let parameter; | ||
for (let i = 0; i< descriptor.parameters.length; i++) { | ||
parameter = descriptor.parameters[i]; | ||
if (parameter.mandatory) { | ||
mandatoryParametersCount+=1; | ||
for (const prop in descriptor.properties) { | ||
if (descriptor.properties[prop].pattern) { | ||
descriptor.properties[prop].patternRe = new RegExp( | ||
descriptor.properties[prop].pattern, | ||
descriptor.properties[prop].patternFlag||'' | ||
); | ||
} | ||
if (!parameter.sanityCheck) { | ||
continue; | ||
} | ||
parameter.sanityCheck.re = new RegExp( | ||
parameter.sanityCheck.reString, | ||
parameter.sanityCheck.reFlag | ||
); | ||
} | ||
@@ -139,0 +214,0 @@ |
const log = require('evillogger')({ ns:'methods/shared' }); | ||
const ENV = require('../env'); | ||
const path = require('path'); | ||
const loki = require('lokijs'); | ||
const config = require('../config'); | ||
const dbs = {}; | ||
const collections = {}; | ||
// http://jsonrpc.org/spec.html#error_object | ||
const ERROR_CODE_PARAMETER = -32602; | ||
@@ -14,3 +18,3 @@ const ERROR_CODE_INTERNAL = -32603; | ||
autosave:true, | ||
autosaveInterval:ENV.DATABASES_AUTOSAVE_INTERVAL | ||
autosaveInterval:config.DATABASES_AUTOSAVE_INTERVAL | ||
}; | ||
@@ -57,3 +61,46 @@ | ||
function getDatabase(databaseName, callback) { | ||
if (!dbs[databaseName]) { | ||
callback(null, undefined); | ||
return; | ||
} | ||
// avoid circular structure: ignore autosaveHandle, persistenceAdapter | ||
callback(null, { | ||
filename:dbs[databaseName].filename, | ||
databaseVersion:dbs[databaseName].databaseVersion, | ||
engineVersion:dbs[databaseName].engineVersion, | ||
autosave:dbs[databaseName].autosave, | ||
autosaveInterval:dbs[databaseName].autosaveInterval, | ||
throttledSaves:dbs[databaseName].throttledSaves, | ||
options:dbs[databaseName].options, | ||
persistenceMethod:dbs[databaseName].persistenceMethod, | ||
persistenceAdapter:typeof dbs[databaseName].persistenceAdapter, | ||
throttledSavePending:dbs[databaseName].throttledSavePending, | ||
verbose:dbs[databaseName].verbose, | ||
ENV:dbs[databaseName].ENV, | ||
name:dbs[databaseName].name | ||
}); | ||
} | ||
function createDatabase(databaseName, databaseOptions, callback) { | ||
const dbPath = path.resolve(config.SLOKI_DIR_DBS+`/${databaseName}.json`); | ||
const options = Object.assign(DEFAULT_DATABASE_OPTIONS, databaseOptions||{}); | ||
options.autoloadCallback = () => { | ||
getDatabase(databaseName, (err, result) => { | ||
callback(null, result); | ||
}); | ||
}; | ||
dbs[databaseName] = new loki(dbPath, options); | ||
// by default, immediate save (force flush) after creating database | ||
config.DATABASES_FORCE_SAVE_ON_CREATE && dbs[databaseName].save(); | ||
} | ||
module.exports = { | ||
getDatabase, | ||
createDatabase, | ||
dbs, | ||
@@ -67,4 +114,3 @@ collections, | ||
ERROR_CODE_PARAMETER, | ||
ERROR_CODE_INTERNAL, | ||
ENV | ||
ERROR_CODE_INTERNAL | ||
}; |
const log = require('evillogger')({ ns:'server' }); | ||
const use = require('abrequire'); | ||
const tcp = require('./transports/tcpJayson'); | ||
const path = require('path'); | ||
const loki = require('./loki'); | ||
const ENV = use('src/env'); | ||
const binaryServer = require('./protocols/binary'); | ||
const jsonRpcServer = require('./protocols/jsonrpc'); | ||
const prettyBytes = require('pretty-bytes'); | ||
const async = require('async'); | ||
const ssl = require('./ssl'); | ||
let config = require('./config'); | ||
let closing = false; | ||
let running = false; | ||
let timerMemoryAlert; | ||
let tcpBinaryServerInstance; | ||
let tlsBinaryServerInstance; | ||
let tcpJsonRpcServerInstance; | ||
let tlsJsonRpcServerInstance; | ||
const memoryAlertInterval = 1000; | ||
function memLimitBytes() { | ||
return config.MEM_LIMIT*1024*1024; | ||
} | ||
function memLimitBytesHuman() { | ||
return prettyBytes(memLimitBytes()); | ||
} | ||
function handleSignals() { | ||
@@ -28,28 +45,106 @@ process.on('SIGINT', handleSignalSIGINT); | ||
function start(options, callback) { | ||
function start(callback) { | ||
if (typeof options === 'function') { | ||
callback = options; | ||
options = null; | ||
} | ||
loki.initialize(); | ||
// already running ! | ||
if (running) { | ||
callback && callback('ERUNNING'); | ||
if (callback) { | ||
callback('ERUNNING'); | ||
} | ||
return; | ||
} | ||
log.info( | ||
'server starting ... (%s:%s)', | ||
ENV.NET_TCP_HOST, | ||
ENV.NET_TCP_PORT | ||
); | ||
tcp.start((err) => { | ||
if (err) { | ||
log.error(err); | ||
return; | ||
config = Object.assign(config, options||{}); | ||
config.SLOKI_DIR_DBS = path.resolve(config.SLOKI_DIR+'/dbs'); | ||
async.series([ | ||
(next) => { | ||
ssl.check(next); | ||
}, | ||
(next) => { | ||
if (!config.TCP_BINARY_ENABLE) { | ||
next(); | ||
return; | ||
} | ||
tcpBinaryServerInstance = new binaryServer({ | ||
HOST:config.TCP_BINARY_HOST, | ||
PORT:config.TCP_BINARY_PORT, | ||
MAX_CLIENTS:config.TCP_BINARY_MAX_CLIENTS, | ||
SSL:false, | ||
SHOW_OPS_INTERVAL:config.SHOW_OPS_INTERVAL | ||
}); | ||
tcpBinaryServerInstance.start(next); | ||
}, | ||
(next) => { | ||
if (!config.TLS_BINARY_ENABLE) { | ||
next(); | ||
return; | ||
} | ||
tlsBinaryServerInstance = new binaryServer({ | ||
HOST:config.TLS_BINARY_HOST, | ||
PORT:config.TLS_BINARY_PORT, | ||
MAX_CLIENTS:config.TLS_BINARY_MAX_CLIENTS, | ||
SSL:true, | ||
SSL_PRIVATE_KEY:config.SSL_PRIVATE_KEY, | ||
SSL_CERTIFICATE:config.SSL_CERTIFICATE, | ||
SSL_CA:config.SSL_CA, | ||
SHOW_OPS_INTERVAL:config.SHOW_OPS_INTERVAL | ||
}); | ||
tlsBinaryServerInstance.start(next); | ||
}, | ||
(next) => { | ||
if (!config.TCP_JSONRPC_ENABLE) { | ||
next(); | ||
return; | ||
} | ||
tcpJsonRpcServerInstance = new jsonRpcServer({ | ||
HOST:config.TCP_JSONRPC_HOST, | ||
PORT:config.TCP_JSONRPC_PORT, | ||
MAX_CLIENTS:config.TCP_JSONRPC_MAX_CLIENTS, | ||
SSL:false, | ||
SHOW_OPS_INTERVAL:config.SHOW_OPS_INTERVAL | ||
}); | ||
tcpJsonRpcServerInstance.start(next); | ||
}, | ||
(next) => { | ||
if (!config.TLS_JSONRPC_ENABLE) { | ||
next(); | ||
return; | ||
} | ||
tlsJsonRpcServerInstance = new jsonRpcServer({ | ||
HOST:config.TLS_JSONRPC_HOST, | ||
PORT:config.TLS_JSONRPC_PORT, | ||
MAX_CLIENTS:config.TLS_JSONRPC_MAX_CLIENTS, | ||
SSL:true, | ||
SSL_PRIVATE_KEY:config.SSL_PRIVATE_KEY, | ||
SSL_CERTIFICATE:config.SSL_CERTIFICATE, | ||
SSL_CA:config.SSL_CA, | ||
SHOW_OPS_INTERVAL:config.SHOW_OPS_INTERVAL | ||
}); | ||
tlsJsonRpcServerInstance.start(next); | ||
} | ||
running = true; | ||
handleSignals(); | ||
callback && callback(); | ||
], (err) => { | ||
if (!err) { | ||
running = true; | ||
handleSignals(); | ||
loki.initialize(); | ||
timerMemoryAlert = setInterval(memoryAlert, memoryAlertInterval); | ||
if (callback) { | ||
callback(); | ||
} | ||
} | ||
}); | ||
} | ||
@@ -59,3 +154,5 @@ | ||
if (!running) { | ||
callback && callback('ENOTRUNNING'); | ||
if (callback) { | ||
callback('ENOTRUNNING'); | ||
} | ||
return; | ||
@@ -66,13 +163,55 @@ } | ||
tcp.stop((err) => { | ||
log.info('server stopped, exiting'); | ||
async.series([ | ||
tcpBinaryServerInstance.stop, | ||
tlsBinaryServerInstance.stop, | ||
tcpJsonRpcServerInstance.stop, | ||
tlsJsonRpcServerInstance.stop | ||
], (err) => { | ||
log.warn('bye'); | ||
running = false; | ||
clearInterval(timerMemoryAlert); | ||
if (callback) { | ||
callback(err); | ||
return; | ||
} | ||
process.exit(err ? 1 : 0); | ||
process.exit(); | ||
}); | ||
} | ||
function dumpMemory(level, prefix, mem) { | ||
log[level]( | ||
'%s rss=%s, allowed %s', | ||
prefix, | ||
prettyBytes(mem.rss), | ||
memLimitBytesHuman(), | ||
); | ||
} | ||
function memoryAlert() { | ||
const mem = process.memoryUsage(); | ||
if (mem.rss>memLimitBytes()) { | ||
if (!config.MEM_LIMIT_REACHED) { | ||
dumpMemory('warn', 'memory: limit reached', mem); | ||
config.MEM_LIMIT_REACHED = true; | ||
} else { | ||
dumpMemory('warn', 'memory: always above limit', mem); | ||
} | ||
} else { | ||
if (config.MEM_LIMIT_REACHED) { | ||
dumpMemory('warn', 'memory: back under limit', mem); | ||
} | ||
config.MEM_LIMIT_REACHED = false; | ||
} | ||
} | ||
if (global.gc) { | ||
log.info(`Garbage collector will run every ${config.GC_INTERVAL} ms`); | ||
setInterval(() => { | ||
global.gc(); | ||
}, config.GC_INTERVAL); | ||
} | ||
dumpMemory('info', 'memory:', process.memoryUsage()); | ||
module.exports = { | ||
@@ -79,0 +218,0 @@ start, |
@@ -1,6 +0,6 @@ | ||
const use = require('abrequire'); | ||
const ENV = use('src/env'); | ||
module.exports = { | ||
tcp:'tcp://'+ENV.NET_TCP_HOST+':'+ENV.NET_TCP_PORT | ||
binary:'binary://localhost:6370', | ||
binarys:'binarys://localhost:6371', | ||
jsonrpc:'jsonrpc://localhost:6372', | ||
jsonrpcs:'jsonrpcs://localhost:6373' | ||
}; |
@@ -8,6 +8,6 @@ const log = require('evillogger')({ ns:'tests' }); | ||
const fs = require('fs-extra'); | ||
const ENV = require('../src/env'); | ||
const config = require('../src/config'); | ||
const homedir = require('os').homedir(); | ||
const tests = {}; | ||
const testFailed = false; | ||
@@ -35,3 +35,3 @@ let testName; | ||
function cleanTestDatabases() { | ||
if (!fs.pathExistsSync(ENV.DATABASES_DIRECTORY)) { | ||
if (!fs.pathExistsSync(config.SLOKI_DIR_DBS)) { | ||
return; | ||
@@ -41,3 +41,3 @@ } | ||
let file; | ||
for (file of klawSync(ENV.DATABASES_DIRECTORY, { depthLimit:0 })) { | ||
for (file of klawSync(config.SLOKI_DIR_DBS, { depthLimit:0 })) { | ||
if (path.basename(file.path).match(/\_\_/)) { | ||
@@ -50,9 +50,4 @@ console.log('removing', file.path); | ||
function endTests() { | ||
cleanTestDatabases(); | ||
if (testFailed) process.exit(-1); | ||
process.exit(0); | ||
} | ||
function runTests() { | ||
function runTests(engine, done) { | ||
prepareTests(); | ||
@@ -69,3 +64,3 @@ | ||
const optionTap = [ | ||
'node_modules/tap/bin/run.js', | ||
path.resolve('./node_modules/tap/bin/run.js'), | ||
'--reporter='+reporter | ||
@@ -75,3 +70,3 @@ ]; | ||
const optionTape = [ | ||
'node_modules/tape/bin/tape' | ||
path.resolve('./node_modules/tape/bin/tape') | ||
]; | ||
@@ -84,12 +79,20 @@ | ||
(test, next) => { | ||
let options; | ||
let args; | ||
if (tester === 'tape') { | ||
options = JSON.parse(JSON.stringify(optionTape)); | ||
args = JSON.parse(JSON.stringify(optionTape)); | ||
} else { | ||
options = JSON.parse(JSON.stringify(optionTap)); | ||
args = JSON.parse(JSON.stringify(optionTap)); | ||
} | ||
options.push(test); | ||
args.push(test); | ||
const s = spawn('node', options, { stdio:'inherit' }); | ||
process.env.SLOKI_SERVER_ENGINE = engine; | ||
process.env.NODE_ENV = 'dev'; | ||
const opts = { | ||
stdio:'inherit', | ||
env:process.env | ||
}; | ||
const s = spawn('node', args, opts); | ||
s.on('close', (code) => { | ||
@@ -101,23 +104,45 @@ if (code != 0) { | ||
}); | ||
s.on('error', (err) => { | ||
console.log(err); | ||
process.exit(255); | ||
}); | ||
}, | ||
() => { | ||
if (process.env.CI) { | ||
server.stop(endTests); | ||
} else { | ||
endTests(); | ||
} | ||
} | ||
done | ||
); | ||
} | ||
if (process.env.CI) { | ||
server.start((err) => { | ||
if (err) { | ||
throw new Error(err); | ||
const options = { | ||
SLOKI_DIR:path.resolve(homedir+'/.slokitest'), | ||
MEM_LIMIT:62 // in Mb | ||
}; | ||
server.start(options, (err) => { | ||
if (err) { | ||
throw new Error(err); | ||
} | ||
async.series([ | ||
(next) => { | ||
cleanTestDatabases(); | ||
runTests('binary', next); | ||
}, | ||
(next) => { | ||
cleanTestDatabases(); | ||
runTests('binarys', next); | ||
}, | ||
(next) => { | ||
cleanTestDatabases(); | ||
runTests('jsonrpc', next); | ||
}, | ||
(next) => { | ||
cleanTestDatabases(); | ||
runTests('jsonrpcs', next); | ||
}, | ||
(next) => { | ||
server.stop(next); | ||
} | ||
setTimeout(runTests, 1000); | ||
], () => { | ||
process.exit(); | ||
}); | ||
} else { | ||
cleanTestDatabases(); | ||
runTests(); | ||
} | ||
}); |
@@ -1,4 +0,5 @@ | ||
require('./client')(__filename, (test, client) => { | ||
require('./client')(__filename, (test, client, end) => { | ||
client.close(); | ||
test.end(); | ||
end(); | ||
}); |
@@ -1,7 +0,8 @@ | ||
require('./client')(__filename, (test, client) => { | ||
require('./client')(__filename, (test, client, end) => { | ||
try { | ||
client.quit((err) => { | ||
client.quit(err => { | ||
test.deepEqual(err, undefined, 'method should not return an error'); | ||
test.pass('should be disconnected by the server'); | ||
test.end(); | ||
end(); | ||
}); | ||
@@ -11,3 +12,4 @@ } catch(e) { | ||
test.end(); | ||
end(); | ||
} | ||
}); |
@@ -1,13 +0,13 @@ | ||
const use = require('abrequire'); | ||
const Client = require('sloki-node-client'); | ||
const endpoint = require('../endpoints').tcp; | ||
const ENV = use('src/env'); | ||
const Client = require('../../../clients/node'); | ||
const config = require('../../src/config'); | ||
const engine = process.env.SLOKI_SERVER_ENGINE||'tcpbinary'; | ||
const endpoint = require('../endpoints')[engine]; | ||
let maxClients = ENV.NET_TCP_MAX_CLIENTS; | ||
const MAX_CLIENTS = config.getMaxClients(engine); | ||
require('./client')(__filename, (test, client) => { | ||
test.test('client1: getMaxClients', (subtest) => { | ||
require('./client')(__filename, (test, client, end) => { | ||
test.test('client1: getMaxClients', subtest => { | ||
client.maxClients((err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
subtest.equal(result, maxClients, 'client1: maxClients should return '+maxClients); | ||
subtest.equal(result, MAX_CLIENTS, 'client1: maxClients should return '+MAX_CLIENTS); | ||
subtest.end(); | ||
@@ -17,7 +17,14 @@ }); | ||
test.test('client1: setMaxClients', (subtest) => { | ||
maxClients=1; | ||
client.maxClients(maxClients, (err, result) => { | ||
test.test('client1: setMaxClients', subtest => { | ||
client.maxClients({ value:'a' }, (err) => { | ||
const expectedError = { code: -32602, message: 'method "maxClients": property "value" should be a number, found string' }; | ||
subtest.deepEqual(err, expectedError, 'method should an return error'); | ||
subtest.end(); | ||
}); | ||
}); | ||
test.test('client1: setMaxClients', subtest => { | ||
client.maxClients({ value:1 }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
subtest.equal(result, maxClients, 'client1: maxClients should be set to '+maxClients); | ||
subtest.equal(result, 1, 'client1: maxClients should be set to 1'); | ||
subtest.end(); | ||
@@ -27,6 +34,6 @@ }); | ||
test.test('client1: getMaxClients', (subtest) => { | ||
test.test('client1: getMaxClients', subtest => { | ||
client.maxClients((err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
subtest.equal(result, maxClients, 'client1: maxClients should be '+maxClients); | ||
subtest.equal(result, 1, 'client1: maxClients should be 1'); | ||
subtest.end(); | ||
@@ -36,7 +43,8 @@ }); | ||
test.test('client2: hit maxClients', (subtest) => { | ||
const client2 = new Client(endpoint); | ||
test.test('client2: hit maxClients', subtest => { | ||
const client2 = new Client(endpoint, { engine }); | ||
client2 | ||
.connect() | ||
.then(() => { | ||
subtest.notOk('connect succeed, should not'); | ||
}) | ||
@@ -46,11 +54,13 @@ .catch((err) => { | ||
subtest.deepEqual(err, expectedErr, 'client2: should return '+JSON.stringify(expectedErr)); | ||
setTimeout(subtest.end, 200); | ||
client2.close(); | ||
setTimeout(subtest.end, 500); | ||
}); | ||
}); | ||
test.test('client1: restore maxClients', (subtest) => { | ||
client.maxClients(ENV.NET_TCP_MAX_CLIENTS, (err, result) => { | ||
test.test('client1: restore maxClients', subtest => { | ||
client.maxClients({ value:MAX_CLIENTS }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
subtest.equal(result, ENV.NET_TCP_MAX_CLIENTS, 'client1: maxClients should be set to '+ENV.NET_TCP_MAX_CLIENTS); | ||
subtest.equal(result, MAX_CLIENTS, 'client1: maxClients should be set to '+MAX_CLIENTS); | ||
subtest.end(); | ||
end(); | ||
}); | ||
@@ -57,0 +67,0 @@ }); |
@@ -1,3 +0,3 @@ | ||
require('./client')(__filename, (test, client) => { | ||
test.test('db', (subtest) => { | ||
require('./client')(__filename, (test, client, end) => { | ||
test.test('db', subtest => { | ||
client.db((err, result) => { | ||
@@ -7,4 +7,5 @@ subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
subtest.end(); | ||
end(); | ||
}); | ||
}); | ||
}); |
const dbName = '__testUse'; | ||
require('./client')(__filename, (test, client) => { | ||
test.test('loadDatabase', (subtest) => { | ||
client.loadDatabase(dbName, (err, result) => { | ||
require('./client')(__filename, (test, client, end) => { | ||
test.test('loadDatabase', subtest => { | ||
// 'database' property got 2 aliases: 'db' and 'd', let's use 'db' | ||
client.loadDatabase({ db:dbName }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
subtest.ok(typeof result, 'object', 'should return database properties'); | ||
subtest.deepEqual(typeof result, 'object', 'should return database properties'); | ||
subtest.end(); | ||
@@ -12,3 +13,3 @@ }); | ||
test.test('db', (subtest) => { | ||
test.test('db', subtest => { | ||
client.db((err, result) => { | ||
@@ -18,4 +19,5 @@ subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
subtest.end(); | ||
end(); | ||
}); | ||
}); | ||
}); |
const dbName = '__testUseWithOptions_'+Date.now(); | ||
require('./client')(__filename, (test, client) => { | ||
test.test('loadDatabase', (subtest) => { | ||
client.loadDatabase(dbName, { autosave:false }, (err, result) => { | ||
require('./client')(__filename, (test, client, end) => { | ||
test.test('loadDatabase', subtest => { | ||
client.loadDatabase({ database:dbName, options:{ autosave:false } }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
@@ -10,2 +10,3 @@ subtest.deepEqual(typeof result, 'object', 'should return database properties'); | ||
subtest.end(); | ||
end(); | ||
}); | ||
@@ -12,0 +13,0 @@ }); |
@@ -1,3 +0,3 @@ | ||
require('./client')(__filename, (test, client) => { | ||
test.test('listDatabases', (subtest) => { | ||
require('./client')(__filename, (test, client, end) => { | ||
test.test('listDatabases', subtest => { | ||
client.listDatabases((err, result) => { | ||
@@ -7,4 +7,5 @@ subtest.deepEqual(typeof result, 'object', 'should return an array'); | ||
subtest.end(); | ||
end(); | ||
}); | ||
}); | ||
}); |
@@ -53,10 +53,10 @@ const dbName = '__testAddCollection'; | ||
require('./client')(__filename, (test, client) => { | ||
client.loadDatabase(dbName, (err, result) => { | ||
require('./client')(__filename, (test, client, end) => { | ||
client.loadDatabase({ database:dbName }, (err, result) => { | ||
test.deepEqual(err, undefined, 'loadDatabase should not return any error'); | ||
test.deepEqual(typeof result, 'object', 'database loaded'); | ||
test.test('addCollection should failed if Collection name is null', (subtest) => { | ||
test.test('addCollection should failed if Collection name is null', subtest => { | ||
client.addCollection(null, (err, result) => { | ||
const expected = { code: ERROR_CODE_PARAMETER, message: 'addCollection: parameter \'Collection name\' is mandatory' }; | ||
const expected = { code: ERROR_CODE_PARAMETER, message: 'method "addCollection": property collection (alias col,c) is mandatory' }; | ||
subtest.deepEqual(err, expected, `should return error ${expected.message}`); | ||
@@ -68,5 +68,5 @@ subtest.deepEqual(result, undefined, 'result should be undefined'); | ||
test.test('addCollection should failed if Collection name is undefined', (subtest) => { | ||
test.test('addCollection should failed if Collection name is undefined', subtest => { | ||
client.addCollection(undefined, (err, result) => { | ||
const expected = { code: ERROR_CODE_PARAMETER, message: 'addCollection: parameter \'Collection name\' is mandatory' }; | ||
const expected = { code: ERROR_CODE_PARAMETER, message: 'method "addCollection": property collection (alias col,c) is mandatory' }; | ||
subtest.deepEqual(err, expected, `should return error ${expected.message}`); | ||
@@ -78,7 +78,8 @@ subtest.deepEqual(result, undefined, 'result should be undefined'); | ||
test.test(`addCollection should create collection '${collectionName}'`, (subtest) => { | ||
client.addCollection(collectionName, (err, result) => { | ||
test.test(`addCollection should create collection '${collectionName}'`, subtest => { | ||
client.addCollection({ collection:collectionName }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
subtest.deepEqual(result, expected, 'should return collection properties'); | ||
subtest.end(); | ||
end(); | ||
}); | ||
@@ -85,0 +86,0 @@ }); |
@@ -5,5 +5,5 @@ const dbName = '__testAddCollectionWithOptions'; | ||
require('./client')(__filename, (test, client) => { | ||
test.test('loadDatabase', (subtest) => { | ||
client.loadDatabase(dbName, (err, result) => { | ||
require('./client')(__filename, (test, client, end) => { | ||
test.test('loadDatabase', subtest => { | ||
client.loadDatabase({ database:dbName }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
@@ -15,7 +15,8 @@ subtest.ok(typeof result, 'object', 'should return database properties'); | ||
test.test('getCollection', (subtest) => { | ||
client.getCollection(collectionName, (err, result) => { | ||
test.test('getCollection', subtest => { | ||
client.getCollection({ collection: collectionName }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
subtest.deepEqual(result, null, 'should return '+collectionName+' properties'); | ||
subtest.end(); | ||
end(); | ||
}); | ||
@@ -22,0 +23,0 @@ }); |
@@ -58,4 +58,4 @@ const dbName = '__testAddCollectionWithOptions'; | ||
require('./client')(__filename, (test, client) => { | ||
client.loadDatabase(dbName, (err, result) => { | ||
require('./client')(__filename, (test, client, end) => { | ||
client.loadDatabase({ database: dbName }, (err, result) => { | ||
@@ -65,4 +65,4 @@ test.deepEqual(err, undefined, 'loadDatabase should not return any error'); | ||
test.test('addCollection', (subtest) => { | ||
client.addCollection(collectionName, collectionOptions, (err, result) => { | ||
test.test('addCollection', subtest => { | ||
client.addCollection({ collection:collectionName, options: collectionOptions }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
@@ -75,3 +75,3 @@ subtest.deepEqual(result, expectedCollectionProperties, 'should return '+collectionName); | ||
test.test('saveDatabase', (subtest) => { | ||
test.test('saveDatabase', subtest => { | ||
client.saveDatabase((err, result) => { | ||
@@ -84,7 +84,8 @@ subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
test.test('getCollection', (subtest) => { | ||
client.getCollection(collectionName, (err, result) => { | ||
test.test('getCollection', subtest => { | ||
client.getCollection({ collection:collectionName }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
subtest.deepEqual(result, expectedCollectionProperties, 'should return '+collectionName+' properties'); | ||
subtest.end(); | ||
end(); | ||
}); | ||
@@ -91,0 +92,0 @@ }); |
@@ -20,7 +20,7 @@ const ERROR_CODE_PARAMETER = -32602; | ||
code: ERROR_CODE_PARAMETER, | ||
message: 'insert: parameter \'Collection name\' is mandatory' | ||
message: 'method "insert": property collection (alias col,c) is mandatory' | ||
}; | ||
require('./client')(__filename, (test, client) => { | ||
client.loadDatabase(dbName, (err, result) => { | ||
require('./client')(__filename, (test, client, end) => { | ||
client.loadDatabase({ database: dbName }, (err, result) => { | ||
@@ -30,3 +30,3 @@ test.deepEqual(err, undefined, 'loadDatabase should not return any error'); | ||
client.addCollection(collectionName, (err, result) => { | ||
client.addCollection({ collection:collectionName }, (err, result) => { | ||
@@ -36,4 +36,4 @@ test.deepEqual(err, undefined, 'addCollection should not return any error'); | ||
test.test('insert should fail if collection name is null', (subtest) => { | ||
client.insert(null, null, (err, result) => { | ||
test.test('insert should fail if collection name is null', subtest => { | ||
client.insert(null, (err, result) => { | ||
subtest.deepEqual(err, expectedErr, `should return error ${expectedErr.message}`); | ||
@@ -45,4 +45,4 @@ subtest.deepEqual(result, undefined, 'result should be undefined'); | ||
test.test('insert should fail if collection name is undefined', (subtest) => { | ||
client.insert(undefined, null, (err, result) => { | ||
test.test('insert should fail if collection name is undefined', subtest => { | ||
client.insert(undefined, (err, result) => { | ||
subtest.deepEqual(err, expectedErr, `should return error ${expectedErr.message}`); | ||
@@ -54,4 +54,4 @@ subtest.deepEqual(result, undefined, 'result should be undefined'); | ||
test.test('insert should create the collection if it does not exist, then insert', (subtest) => { | ||
client.insert(collectionNameNotExists, doc, (err, result) => { | ||
test.test('insert should create the collection if it does not exist, then insert', subtest => { | ||
client.insert({ collection:collectionNameNotExists, document:doc }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
@@ -64,4 +64,4 @@ result.meta.created = typeof result.meta.created === 'number'; | ||
test.test('insert should return document', (subtest) => { | ||
client.insert(collectionName, doc, (err, result) => { | ||
test.test('insert should return document', subtest => { | ||
client.insert({ collection:collectionName, document:doc }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
@@ -74,6 +74,7 @@ result.meta.created = typeof result.meta.created === 'number'; | ||
test.test('insert without callback', (subtest) => { | ||
client.insert(collectionName, doc); | ||
test.test('insert without callback (lazy mode)', subtest => { | ||
client.insert({ collection:collectionName, document: doc }, { lazy:true }); | ||
subtest.pass('pass'); | ||
subtest.end(); | ||
end(); | ||
}); | ||
@@ -80,0 +81,0 @@ }); |
@@ -5,4 +5,4 @@ const dbName = '__testInsert401_'+Date.now(); | ||
require('./client')(__filename, (test, client) => { | ||
client.loadDatabase(dbName, (err, result) => { | ||
require('./client')(__filename, (test, client, end) => { | ||
client.loadDatabase({ database:dbName }, (err, result) => { | ||
@@ -12,3 +12,3 @@ test.deepEqual(err, undefined, 'loadDatabase should not return any error'); | ||
client.addCollection(collectionName, (err, result) => { | ||
client.addCollection({ collection:collectionName }, (err, result) => { | ||
@@ -18,4 +18,4 @@ test.deepEqual(err, undefined, 'addCollection should not return any error'); | ||
test.test('insert should return undefined', (subtest) => { | ||
client.insert(collectionName, doc, { sret:null }, (err, result) => { | ||
test.test('insert should return undefined', subtest => { | ||
client.insert({ collection:collectionName, document:doc, options:{ sret:null } }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
@@ -27,4 +27,4 @@ subtest.deepEqual(result, undefined, 'should return undefined'); | ||
test.test('insert should return 1', (subtest) => { | ||
client.insert(collectionName, doc, { sret:'01' }, (err, result) => { | ||
test.test('insert should return 1', subtest => { | ||
client.insert({ collection:collectionName, document:doc, options: { sret:'01' } }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
@@ -36,4 +36,4 @@ subtest.deepEqual(result, 1, 'should return 1'); | ||
test.test('insert should return true', (subtest) => { | ||
client.insert(collectionName, doc, { sret:'bool' }, (err, result) => { | ||
test.test('insert should return true', subtest => { | ||
client.insert({ collection:collectionName, document:doc, options:{ sret:'bool' } }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
@@ -45,7 +45,8 @@ subtest.deepEqual(result, true, 'should return true'); | ||
test.test('insert should return id', (subtest) => { | ||
client.insert(collectionName, doc, { sret:'id' }, (err, result) => { | ||
test.test('insert should return id', subtest => { | ||
client.insert({ collection:collectionName, document:doc, options:{ sret:'id' } }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
subtest.deepEqual(typeof result, 'number', 'should return true'); | ||
subtest.end(); | ||
end(); | ||
}); | ||
@@ -52,0 +53,0 @@ }); |
@@ -21,4 +21,4 @@ const dbName = '__testGet_410_'+Date.now(); | ||
require('./client')(__filename, (test, client) => { | ||
client.loadDatabase(dbName, (err, result) => { | ||
require('./client')(__filename, (test, client, end) => { | ||
client.loadDatabase({ database: dbName }, (err, result) => { | ||
@@ -28,4 +28,4 @@ test.deepEqual(err, undefined, 'loadDatabase should not return any error'); | ||
test.test('insert should return document', (subtest) => { | ||
client.insert(collectionName, doc, (err, result) => { | ||
test.test('insert should return document', subtest => { | ||
client.insert({ collection:collectionName, document:doc }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
@@ -38,4 +38,4 @@ result.meta.created = typeof result.meta.created === 'number'; | ||
test.test('get should return error when collection does not exist', (subtest) => { | ||
client.get('unexistingCollection', 1, (err, result) => { | ||
test.test('get should return error when collection does not exist', subtest => { | ||
client.get({ collection:'unexistingCollection', id:1 }, (err, result) => { | ||
const expectedErr = { | ||
@@ -51,4 +51,4 @@ code: ERROR_CODE_PARAMETER, | ||
test.test('get should return document', (subtest) => { | ||
client.get(collectionName, 1, (err, result) => { | ||
test.test('get should return document', subtest => { | ||
client.get({ collection:collectionName, id:1 }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
@@ -61,7 +61,8 @@ result.meta.created = typeof result.meta.created === 'number'; | ||
test.test('get on a non existing document by id should return null', (subtest) => { | ||
client.get(collectionName, 10, (err, result) => { | ||
test.test('get on a non existing document by id should return null', subtest => { | ||
client.get({ collection:collectionName, id:10 }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
subtest.deepEqual(result, null, `should return error ${JSON.stringify(expectedErr)}`); | ||
subtest.end(); | ||
end(); | ||
}); | ||
@@ -68,0 +69,0 @@ }); |
@@ -13,12 +13,17 @@ const dbName = '__testRemove_420_'+Date.now(); | ||
code: -32602, | ||
message: 'remove: number of parameters should be at least 2' | ||
message: 'method "remove": mandatory properties are missing (at least "collection, document" OR "collection, id")' | ||
}; | ||
require('./client')(__filename, (test, client) => { | ||
client.loadDatabase(dbName, (err, result) => { | ||
const expectedErr3 = { | ||
code: -32602, | ||
message: 'method "remove": mandatory properties conflict, please specify properties "collection, document" OR "collection, id"' | ||
}; | ||
require('./client')(__filename, (test, client, end) => { | ||
client.loadDatabase({ database:dbName }, (err, result) => { | ||
test.deepEqual(err, undefined, 'loadDatabase should not return any error'); | ||
test.deepEqual(typeof result, 'object', 'database loaded'); | ||
client.insert(collectionName, doc1, (err, result) => { | ||
client.insert({ collection:collectionName, document:doc1 }, (err, result) => { | ||
@@ -28,4 +33,4 @@ test.deepEqual(err, undefined, 'insert should not return any error'); | ||
test.test('remove a document by id should return removed document', (subtest) => { | ||
client.remove(collectionName, 1, (err, result) => { | ||
test.test('remove a document by id should return removed document', subtest => { | ||
client.remove({ collection:collectionName, id:1 }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
@@ -37,4 +42,4 @@ subtest.deepEqual(result, doc1, `should return ${JSON.stringify(doc1)}`); | ||
test.test('remove a non existing document by id should return an error', (subtest) => { | ||
client.remove(collectionName, 2, (err, result) => { | ||
test.test('remove a non existing document by id should return an error', subtest => { | ||
client.remove({ collection:collectionName, id:2 }, (err, result) => { | ||
subtest.deepEqual(err, expectedErr1, `should return error ${JSON.stringify(expectedErr1)}`); | ||
@@ -46,4 +51,4 @@ subtest.deepEqual(result, undefined, 'result should be undefined'); | ||
test.test('missing doc or id should return an error', (subtest) => { | ||
client.remove(collectionName, (err, result) => { | ||
test.test('missing document and id should return an error', subtest => { | ||
client.remove({ collection:collectionName }, (err, result) => { | ||
subtest.deepEqual(err, expectedErr2, `should return error ${JSON.stringify(expectedErr2)}`); | ||
@@ -55,7 +60,16 @@ subtest.deepEqual(result, undefined, 'result should be undefined'); | ||
test.test('empty doc {} should return an error', (subtest) => { | ||
client.remove(collectionName, {}, (err, result) => { | ||
test.test('document and id specified should return an error', subtest => { | ||
client.remove({ collection:collectionName, document:{}, id:1 }, (err, result) => { | ||
subtest.deepEqual(err, expectedErr3, `should return error ${JSON.stringify(expectedErr3)}`); | ||
subtest.deepEqual(result, undefined, 'result should be undefined'); | ||
subtest.end(); | ||
}); | ||
}); | ||
test.test('empty doc {} should return an error', subtest => { | ||
client.remove({ collection:collectionName, document:{} }, (err, result) => { | ||
subtest.deepEqual(err, expectedErr1, `should return error ${JSON.stringify(expectedErr1)}`); | ||
subtest.deepEqual(result, undefined, 'result should be undefined'); | ||
subtest.end(); | ||
end(); | ||
}); | ||
@@ -62,0 +76,0 @@ }); |
@@ -18,4 +18,4 @@ const dbName = '__testFind_430_'+Date.now(); | ||
require('./client')(__filename, (test, client) => { | ||
client.loadDatabase(dbName, (err, result) => { | ||
require('./client')(__filename, (test, client, end) => { | ||
client.loadDatabase({ database:dbName }, (err, result) => { | ||
@@ -25,3 +25,3 @@ test.deepEqual(err, undefined, 'loadDatabase should not return any error'); | ||
client.insert(collectionName, doc, (err, result) => { | ||
client.insert({ collection:collectionName, document: doc }, (err, result) => { | ||
@@ -31,4 +31,4 @@ test.deepEqual(err, undefined, 'insert should not return any error'); | ||
test.test('find should return array of doc', (subtest) => { | ||
client.find(collectionName, doc, (err, result) => { | ||
test.test('find without filters should return array of documents', subtest => { | ||
client.find({ collection:collectionName }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
@@ -41,7 +41,17 @@ result[0].meta.created = typeof result[0].meta.created === 'number'; | ||
test.test('find should return [] if no result', (subtest) => { | ||
client.find(collectionName, { foo:'bar2' }, (err, result) => { | ||
test.test('find with filters should return array of documents', subtest => { | ||
client.find({ collection:collectionName, filters:doc }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
result[0].meta.created = typeof result[0].meta.created === 'number'; | ||
subtest.deepEqual(result, expected, `should return ${JSON.stringify(expected)}`); | ||
subtest.end(); | ||
}); | ||
}); | ||
test.test('find should return [] if no result', subtest => { | ||
client.find({ collection:collectionName, filters:{ foo:'bar2' } }, (err, result) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
subtest.deepEqual(result, [], `should return ${JSON.stringify([])}`); | ||
subtest.end(); | ||
end(); | ||
}); | ||
@@ -48,0 +58,0 @@ }); |
@@ -17,4 +17,4 @@ const dbName = '__testUpdate_440_'+Date.now(); | ||
require('./client')(__filename, (test, client) => { | ||
client.loadDatabase(dbName, (err, result) => { | ||
require('./client')(__filename, (test, client, end) => { | ||
client.loadDatabase({ database:dbName }, (err, result) => { | ||
@@ -24,3 +24,3 @@ test.deepEqual(err, undefined, 'loadDatabase should not return any error'); | ||
client.insert(collectionName, doc, (err, myDoc) => { | ||
client.insert({ collection:collectionName, document:doc }, (err, myDoc) => { | ||
@@ -30,7 +30,7 @@ test.deepEqual(err, undefined, 'insert should not return any error'); | ||
test.test('update should return new document', (subtest) => { | ||
test.test('update should return new document', subtest => { | ||
myDoc.foo = 'bar2'; | ||
client.update(collectionName, myDoc, (err, newDoc) => { | ||
client.update({ collection:collectionName, document:myDoc }, (err, newDoc) => { | ||
subtest.deepEqual(err, undefined, 'method should not return an error'); | ||
@@ -41,2 +41,3 @@ newDoc.meta.created = typeof newDoc.meta.created === 'number'; | ||
subtest.end(); | ||
end(); | ||
}); | ||
@@ -43,0 +44,0 @@ }); |
const tap = require('../tap'); | ||
const use = require('abrequire'); | ||
const Client = require('sloki-node-client'); | ||
const ENV = use('src/env'); | ||
const endpoint = require('../endpoints').tcp; | ||
const Client = require('../../../clients/node'); | ||
const config = require('../../src/config'); | ||
const endpoints = require('../endpoints'); | ||
const path = require('path'); | ||
const engine = process.env.SLOKI_SERVER_ENGINE||'tcpbinary'; | ||
module.exports = (title, callback) => { | ||
const tcpClient = new Client(endpoint, { applicationLayer:'jayson' }); | ||
const tcpClient = new Client(endpoints[engine], { engine }); | ||
function end() { | ||
tcpClient.close(); | ||
} | ||
tap.test( | ||
path.basename(title), | ||
{ | ||
timeout:ENV.DATABASES_AUTOSAVE_INTERVAL*3 | ||
}, | ||
(t) => { | ||
{ timeout:config.DATABASES_AUTOSAVE_INTERVAL*3 }, | ||
t => { | ||
tcpClient.on('error', (err) => { | ||
tcpClient.on('error', err => { | ||
t.fail('socket error', err); | ||
@@ -26,5 +29,5 @@ t.end(); | ||
.connect() | ||
.then((err) => { | ||
t.deepEqual(err, undefined, 'should be connected'); | ||
callback(t, tcpClient); | ||
.then(err => { | ||
t.deepEqual(err, undefined, `should be connected (${engine})`); | ||
callback(t, tcpClient, end); | ||
}); | ||
@@ -34,8 +37,2 @@ } | ||
tap.test('close client', (t) => { | ||
tcpClient.close(); | ||
t.pass('client closed'); | ||
t.end(); | ||
process.exit(0); | ||
}); | ||
}; |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
126389
3
67
2895
346
16
9
5
+ Addedasync@2.6.1
+ Addedmissive@3.0.1
+ Addedselfsigned@1.10.4
+ Addedasync@2.6.1(transitive)
+ Addedcolors@1.2.1(transitive)
+ Addedevillogger@1.2.2(transitive)
+ Addedfringe@1.1.1(transitive)
+ Addedminimist@1.2.0(transitive)
+ Addedmissive@3.0.1(transitive)
+ Addednode-forge@0.7.5(transitive)
+ Addedpretty-bytes@5.1.0(transitive)
+ Addedselfsigned@1.10.4(transitive)
+ Addedsprintf-js@1.1.1(transitive)
- Removedabrequire@0.0.4
- Removedsloki-node-client@0.0.6
- Removedabrequire@0.0.4(transitive)
- Removedasync@2.6.4(transitive)
- Removedcluster-key-slot@1.1.2(transitive)
- Removedcolors@1.4.0(transitive)
- Removeddebug@4.4.0(transitive)
- Removeddenque@1.5.1(transitive)
- Removedevillogger@1.4.5(transitive)
- Removedhyperid@2.3.1(transitive)
- Removedioredis@4.27.8(transitive)
- Removedlodash.defaults@4.2.0(transitive)
- Removedlodash.flatten@4.4.0(transitive)
- Removedlodash.isarguments@3.1.0(transitive)
- Removedmerge@2.1.1(transitive)
- Removedminimist@1.2.5(transitive)
- Removedms@2.1.3(transitive)
- Removedp-map@2.1.0(transitive)
- Removedpretty-bytes@5.6.0(transitive)
- Removedredis-commands@1.7.0(transitive)
- Removedredis-errors@1.2.0(transitive)
- Removedredis-parser@3.0.0(transitive)
- Removedsloki-node-client@0.0.6(transitive)
- Removedsprintf-js@1.1.2(transitive)
- Removedstandard-as-callback@2.1.0(transitive)
- Removeduuid@8.3.2(transitive)
- Removeduuid-parse@1.1.0(transitive)
Updatedevillogger@1.2.2
Updatedfs-extra@7.0.1
Updatedjayson@2.1.2
Updatedklaw-sync@6.0.0
Updatedpretty-bytes@5.1.0