diff --git a/.eslintignore b/.eslintignore index 581c5c65e..4a5064d0d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,13 +3,20 @@ src/**/*.d.ts src/**/*.js.map src/Kuzzle.js +src/KuzzleError.js src/controllers/Auth.js src/controllers/Document.js src/controllers/Base.js src/core/security/User.js src/core/security/Profile.js src/core/security/Role.js +src/protocols/abstract/Base.js +src/protocols/abstract/Realtime.js +src/protocols/Http.js +src/protocols/WebSocket.js +src/protocols/index.js src/utils/interfaces.js +src/core/KuzzleEventEmitter.js src/core/searchResult/SearchResultBase.js src/core/searchResult/Document.js src/core/searchResult/Profile.js diff --git a/.gitignore b/.gitignore index d0ef8a16d..875b9e4c4 100644 --- a/.gitignore +++ b/.gitignore @@ -31,13 +31,20 @@ test-*.js *.js.map index.js src/Kuzzle.js +src/KuzzleError.js src/controllers/Auth.js src/controllers/Document.js src/controllers/Base.js src/core/security/User.js src/core/security/Profile.js src/core/security/Role.js +src/protocols/abstract/Base.js +src/protocols/abstract/Realtime.js +src/protocols/Http.js +src/protocols/WebSocket.js +src/protocols/index.js src/utils/interfaces.js +src/core/KuzzleEventEmitter.js src/core/searchResult/SearchResultBase.js src/core/searchResult/Document.js src/core/searchResult/Profile.js diff --git a/doc/7/core-classes/kuzzle-error/properties/index.md b/doc/7/core-classes/kuzzle-error/properties/index.md index 1b5655e92..63b07a4f9 100644 --- a/doc/7/core-classes/kuzzle-error/properties/index.md +++ b/doc/7/core-classes/kuzzle-error/properties/index.md @@ -14,3 +14,6 @@ order: 10 | `message` |
string
| Error message | | `status` |
number
| Error status code | | `stack` |
string
| Error stacktrace (only in development mode) | +| `id` |
string
| Error unique identifier | +| `code` |
string
| Error unique code | + diff --git a/doc/7/essentials/error-handling/index.md b/doc/7/essentials/error-handling/index.md index f19e943a8..fa56caac4 100644 --- a/doc/7/essentials/error-handling/index.md +++ b/doc/7/essentials/error-handling/index.md @@ -15,7 +15,9 @@ All SDK methods return a promise, that can be rejected with a `KuzzleError` valu | Property | Type | Description | | -------- | ----------------- | ------------------------------------------------------------------------------------------ | | `status` |
number
| Status following [HTTP Standards](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) | -| `stack` |
string
| Error stacktrace (Only in development mode) | +| `stack` |
string
| Error stacktrace (Only in development mode) | `id` |
string
| Error unique identifier | +| `code` |
string
| Error unique code | + You can find a detailed list of possible errors messages and statuses in the [documentation API](/core/2/api/essentials/error-handling). diff --git a/doc/7/protocols/http/constructor/index.md b/doc/7/protocols/http/constructor/index.md index d5ff3892f..60609c4fc 100644 --- a/doc/7/protocols/http/constructor/index.md +++ b/doc/7/protocols/http/constructor/index.md @@ -30,7 +30,8 @@ Http protocol connection options. | Property | Type
(default) | Description | | --------------- | -------------------------------- | ----------------------------------- | | `port` |
number

(`7512`) | Kuzzle server port | -| `sslConnection` |
boolean

(`false`) | Use SSL to connect to Kuzzle server | +| `sslConnection` |
boolean

(`false`) | Use SSL to connect to Kuzzle server | +| `ssl` |
boolean

(`false`) | Use SSL to connect to Kuzzle server. Defaults to `true` for ports 443 and 7443. | | `customRoutes` |
object

(`{}`) | Add custom routes | | `timeout` |
number

(`0`) | Connection timeout in milliseconds (`0` means no timeout) | diff --git a/doc/7/protocols/websocket/constructor/index.md b/doc/7/protocols/websocket/constructor/index.md index 736df3deb..5393f00e2 100644 --- a/doc/7/protocols/websocket/constructor/index.md +++ b/doc/7/protocols/websocket/constructor/index.md @@ -31,9 +31,10 @@ WebSocket protocol connection options. | ------------------- | -------------------------------- | -------------------------------------------------------------------------------------------- | | `autoReconnect` |
boolean

(`true`) | Automatically reconnect to kuzzle after a `disconnected` event | | `port` |
number

(`7512`) | Kuzzle server port | -| `headers` |
object
(`{}`) | Connection HTTP headers (e.g. origin, subprotocols, ...)
**(Not supported by browsers)** | +| `headers` |
object
(`{}`) | Connection custom HTTP headers (e.g. origin, subprotocols, ...)
**(Not supported by browsers)** | | `reconnectionDelay` |
number

(`1000`) | Number of milliseconds between reconnection attempts | -| `sslConnection` |
boolean

(`false`) | Use SSL to connect to Kuzzle server | +| `sslConnection` |
boolean

(`false`) | Use SSL to connect to Kuzzle server | +| `ssl` |
boolean

(`false`) | Use SSL to connect to Kuzzle server. Defaults to `true` for ports 443 and 7443. | ## Return diff --git a/package.json b/package.json index 9e6a88c71..781b4ee45 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,9 @@ "test:functional": "cucumber-js --exit --fail-fast", "test:lint": "npm run test:lint:js && npm run test:lint:ts", "test:lint:js": "eslint --max-warnings=0 ./src ./test ./features", + "test:lint:js:fix": "eslint --max-warnings=0 ./src ./test ./features --fix", "test:lint:ts": "eslint ./src --ext .ts --config .eslintc-ts.json", + "test:lint:ts:fix": "eslint ./src --ext .ts --config .eslintc-ts.json --fix", "build": "npm run build-ts && node build.js", "build-ts": "tsc --build tsconfig.json", "doc": "docker-compose -f doc/docker-compose.yml up", diff --git a/src/Kuzzle.ts b/src/Kuzzle.ts index 98a27138e..6d25a3fde 100644 --- a/src/Kuzzle.ts +++ b/src/Kuzzle.ts @@ -693,7 +693,4 @@ Discarded request: ${JSON.stringify(request)}`)); dequeuingProcess(); } - } - -module.exports = { Kuzzle }; diff --git a/src/KuzzleError.js b/src/KuzzleError.js deleted file mode 100644 index fd9ee4ce3..000000000 --- a/src/KuzzleError.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -class KuzzleError extends Error { - constructor (apiError) { - super(apiError.message); - - this.status = apiError.status; - this.stack = apiError.stack; - this.id = apiError.id; - this.code = apiError.code; - - // PartialError - if (this.status === 206) { - this.errors = apiError.errors; - this.count = apiError.count; - } - } -} - -module.exports = KuzzleError; diff --git a/src/KuzzleError.ts b/src/KuzzleError.ts new file mode 100644 index 000000000..b65910f2c --- /dev/null +++ b/src/KuzzleError.ts @@ -0,0 +1,51 @@ +'use strict'; + +/** + * Standard Kuzzle error. + * + * @see https://docs.kuzzle.io/core/2/api/essentials/error-handling/ + */ +export class KuzzleError extends Error { + /** + * Http status code + */ + public status: number; + /** + * Stacktrace (only if NODE_ENV=development) + */ + public stack?: string; + /** + * Unique ID + */ + public id: string; + /** + * Code + */ + public code: number; + + /** + * Associated errors + * (PartialError only) + */ + public errors?: Array; + /** + * Number of associated errors + * (PartialError only) + */ + public count?: number; + + constructor (apiError) { + super(apiError.message); + + this.status = apiError.status; + this.stack = apiError.stack; + this.id = apiError.id; + this.code = apiError.code; + + // PartialError + if (this.status === 206) { + this.errors = apiError.errors; + this.count = apiError.count; + } + } +} diff --git a/src/controllers/Auth.ts b/src/controllers/Auth.ts index 7ae066f9e..9ed867c18 100644 --- a/src/controllers/Auth.ts +++ b/src/controllers/Auth.ts @@ -520,5 +520,3 @@ export class AuthController extends BaseController { }); } } - -module.exports = { AuthController }; diff --git a/src/controllers/Base.ts b/src/controllers/Base.ts index b67d63f58..8115fc82a 100644 --- a/src/controllers/Base.ts +++ b/src/controllers/Base.ts @@ -40,5 +40,3 @@ export class BaseController { return this._kuzzle.query(request, options); } } - -module.exports = { BaseController }; diff --git a/src/controllers/Document.ts b/src/controllers/Document.ts index 34141a911..88066b4a7 100644 --- a/src/controllers/Document.ts +++ b/src/controllers/Document.ts @@ -801,5 +801,3 @@ export class DocumentController extends BaseController { .then(response => response.result); } } - -module.exports = { DocumentController }; diff --git a/src/core/KuzzleEventEmitter.js b/src/core/KuzzleEventEmitter.ts similarity index 92% rename from src/core/KuzzleEventEmitter.js rename to src/core/KuzzleEventEmitter.ts index e88ee825c..1003a5ce6 100644 --- a/src/core/KuzzleEventEmitter.js +++ b/src/core/KuzzleEventEmitter.ts @@ -1,12 +1,20 @@ class Listener { + public fn: (...any) => any; + public once: boolean; + constructor(fn, once = false) { this.fn = fn; this.once = once; } } -class KuzzleEventEmitter { - constructor() { +/** + * @todo proper TS conversion + */ +export class KuzzleEventEmitter { + private _events: Map>; + + constructor () { this._events = new Map(); } @@ -98,7 +106,7 @@ class KuzzleEventEmitter { return this; } - removeAllListeners (eventName) { + removeAllListeners (eventName?: string) { if (eventName) { this._events.delete(eventName); } diff --git a/src/core/searchResult/SearchResultBase.ts b/src/core/searchResult/SearchResultBase.ts index b183d54b2..c48ea9c71 100644 --- a/src/core/searchResult/SearchResultBase.ts +++ b/src/core/searchResult/SearchResultBase.ts @@ -189,6 +189,3 @@ export class SearchResultBase implements SearchResult { return nextSearchResult; } } - - -module.exports = { SearchResultBase }; diff --git a/src/protocols/Http.js b/src/protocols/Http.ts similarity index 78% rename from src/protocols/Http.js rename to src/protocols/Http.ts index e7f1d86fe..95e79574a 100644 --- a/src/protocols/Http.js +++ b/src/protocols/Http.ts @@ -1,10 +1,35 @@ 'use strict'; -const staticHttpRoutes = require('./routes.json'); -const { KuzzleAbstractProtocol } = require('./abstract/Base'); +import staticHttpRoutes from './routes.json'; +import { KuzzleAbstractProtocol } from './abstract/Base'; +import { HttpRoutes, JSONObject, KuzzleRequest } from '../utils/interfaces'; -class HttpProtocol extends KuzzleAbstractProtocol { - constructor(host, options = {}) { +export default class HttpProtocol extends KuzzleAbstractProtocol { + private _routes: HttpRoutes; + private _timeout: number; + private _customRoutes: HttpRoutes; + + /** + * @param host Kuzzle server hostname or IP + * @param options Http connection options + * - `customRoutes` Add custom routes + * - `port` Kuzzle server port (default: `7512`) + * - `ssl` Use SSL to connect to Kuzzle server. Default `false` unless port is 443 or 7443. + * - `timeout` Connection timeout in milliseconds (default: `0`) + */ + constructor( + host: string, + options: { + port?: number; + /** + * @deprecated Use `ssl` instead + */ + sslConnection?: boolean; + ssl?: boolean; + customRoutes?: HttpRoutes; + timeout?: number + } = {} + ) { super(host, options, 'http'); if (typeof host !== 'string' || host === '') { @@ -15,10 +40,10 @@ class HttpProtocol extends KuzzleAbstractProtocol { this._timeout = options.timeout || 0; - this.customRoutes = options.customRoutes || {}; + this._customRoutes = options.customRoutes || {}; - for (const controller of Object.keys(this.customRoutes)) { - const definition = this.customRoutes[controller]; + for (const controller of Object.keys(this._customRoutes)) { + const definition = this._customRoutes[controller]; for (const action of Object.keys(definition)) { const route = definition[action]; @@ -35,35 +60,49 @@ class HttpProtocol extends KuzzleAbstractProtocol { } } - // @deprecated + /** + * @deprecated Use `routes` instead + */ get http () { return this.routes; } - get routes () { + /** + * Returns a list of available routes + */ + get routes (): HttpRoutes { return this._routes; } - get protocol () { + /** + * `http` or `https` + */ + get protocol (): string { return this.ssl ? 'https' : 'http'; } - get connected () { + /** + * Always returns `true` + */ + get connected (): true { return true; } - get timeout () { + /** + * Connection timeout in milliseconds + */ + get timeout (): number { return this._timeout; } - set timeout (timeout) { + set timeout (timeout: number) { this._timeout = timeout; } /** * Connect to the server */ - connect () { + connect (): Promise { if (this.state === 'ready') { return Promise.resolve(); } @@ -92,7 +131,6 @@ class HttpProtocol extends KuzzleAbstractProtocol { .then(({ result: res, error: err }) => { if (! err) { this._routes = this._constructRoutes(res.serverInfo.kuzzle.api.routes); - this._staticRoutes = false; return; } @@ -114,13 +152,13 @@ class HttpProtocol extends KuzzleAbstractProtocol { throw error; }) .then(() => { - this._routes = Object.assign(this._routes, this.customRoutes); + this._routes = Object.assign(this._routes, this._customRoutes); // Client is ready this.clientConnected(); }) .catch(err => { - const connectionError = new Error(`Unable to connect to kuzzle server at ${this.host}:${this.port}`); + const connectionError: any = new Error(`Unable to connect to kuzzle server at ${this.host}:${this.port}`); connectionError.internal = err; this.emit('networkError', connectionError); @@ -134,19 +172,19 @@ class HttpProtocol extends KuzzleAbstractProtocol { * @param {Object} data * @returns {Promise} */ - send (data, options = {}) { - const route = this.routes[data.controller] - && this.routes[data.controller][data.action]; + send (request: KuzzleRequest, options: JSONObject = {}) { + const route = this.routes[request.controller] + && this.routes[request.controller][request.action]; if (! route) { - const error = new Error(`No URL found for "${data.controller}:${data.action}".`); - this.emit(data.requestId, { status: 400, error }); + const error = new Error(`No URL found for "${request.controller}:${request.action}".`); + this.emit(request.requestId, { status: 400, error }); return; } const method = options.verb || route.verb; - const payload = { + const payload: any = { action: undefined, body: undefined, collection: undefined, @@ -160,8 +198,8 @@ class HttpProtocol extends KuzzleAbstractProtocol { }; const queryArgs = {}; - for (const key of Object.keys(data)) { - const value = data[key]; + for (const key of Object.keys(request)) { + const value = request[key]; if (key === 'body') { if (method === 'GET') { @@ -191,7 +229,7 @@ class HttpProtocol extends KuzzleAbstractProtocol { let matches = regex.exec(url); while (matches) { - const urlParam = data[ matches[1] ]; + const urlParam = request[ matches[1] ]; // check if an url param is missing (eg: "/:index/_create) if (!urlParam) { @@ -201,9 +239,9 @@ class HttpProtocol extends KuzzleAbstractProtocol { return; } - url = url.replace(regex, `/${encodeURIComponent(data[matches[1]])}`); + url = url.replace(regex, `/${encodeURIComponent(request[matches[1]])}`); - delete(queryArgs[ matches[1] ]); + delete queryArgs[matches[1]]; matches = regex.exec(url); } @@ -242,10 +280,11 @@ class HttpProtocol extends KuzzleAbstractProtocol { .catch(error => this.emit(payload.requestId, {error})); } - _sendHttpRequest (method, path, payload = {}) { + _sendHttpRequest (method, path, payload: any = {}) { if (typeof XMLHttpRequest === 'undefined') { // NodeJS implementation, using http.request: + // eslint-disable-next-line @typescript-eslint/no-var-requires const httpClient = require('min-req-promise'); if (path[0] !== '/') { @@ -328,8 +367,8 @@ class HttpProtocol extends KuzzleAbstractProtocol { return routes; }, {}); - for (const controller of Object.keys(this.customRoutes)) { - apiRoutes[controller] = this.customRoutes[controller]; + for (const controller of Object.keys(this._customRoutes)) { + apiRoutes[controller] = this._customRoutes[controller]; } return apiRoutes; @@ -371,5 +410,3 @@ function getCorrectRoute (routes) { // will be in the query string return sameLength ? getRoute : shortestRoute; } - -module.exports = HttpProtocol; diff --git a/src/protocols/WebSocket.js b/src/protocols/WebSocket.ts similarity index 67% rename from src/protocols/WebSocket.js rename to src/protocols/WebSocket.ts index f2d05e1fb..25acbebe3 100644 --- a/src/protocols/WebSocket.js +++ b/src/protocols/WebSocket.ts @@ -1,11 +1,59 @@ 'use strict'; -const KuzzleError = require('../KuzzleError'); -const BaseProtocolRealtime = require('./abstract/Realtime'); +import { KuzzleError } from '../KuzzleError'; +import { BaseProtocolRealtime } from './abstract/Realtime'; +import { JSONObject, KuzzleRequest } from '../utils/interfaces'; -class WebSocketProtocol extends BaseProtocolRealtime { +export default class WebSocketProtocol extends BaseProtocolRealtime { + private WebSocketClient: any; + private options: any; + private client: any; + private lasturl: any; - constructor(host, options = {}) { + /** + * Automatically reconnect after a connection loss + */ + public autoReconnect: boolean; + /** + * `true` if the socket is open + */ + public connected: boolean; + /** + * Kuzzle server host or IP + */ + public host: string; + /** + * Kuzzle server port + */ + public port: number; + /** + * `true` if ssl is active + */ + public ssl: boolean; + + /** + * @param host Kuzzle server hostname or IP + * @param options WebSocket connection options + * - `autoReconnect` Automatically reconnect to kuzzle after a `disconnected` event. (default: `true`) + * - `port` Kuzzle server port (default: `7512`) + * - `headers` Connection custom HTTP headers (Not supported by browsers) + * - `reconnectionDelay` Number of milliseconds between reconnection attempts (default: `1000`) + * - `ssl` Use SSL to connect to Kuzzle server. Default `false` unless port is 443 or 7443. + */ + constructor( + host: string, + options: { + autoReconnect?: boolean; + port?: number; + headers?: JSONObject; + reconnectionDelay?: number; + /** + * @deprecated Use `ssl` instead + */ + sslConnection?: boolean; + ssl?: boolean; + } = {} + ) { super(host, options, 'ws'); if (typeof host !== 'string' || host === '') { @@ -39,7 +87,7 @@ class WebSocketProtocol extends BaseProtocolRealtime { /** * Connect to the websocket server */ - connect () { + connect (): Promise { return new Promise((resolve, reject) => { const url = `${this.ssl ? 'wss' : 'ws'}://${this.host}:${this.port}`; @@ -79,7 +127,7 @@ class WebSocketProtocol extends BaseProtocolRealtime { // do not forward a connection close error if no // connection has been previously established else if (this.wasConnected) { - const error = new Error(reason); + const error: any = new Error(reason); error.status = status; this.clientNetworkError(error); @@ -127,9 +175,9 @@ class WebSocketProtocol extends BaseProtocolRealtime { * * @param {Object} payload */ - send (payload) { + send (request: KuzzleRequest) { if (this.client && this.client.readyState === this.client.OPEN) { - this.client.send(JSON.stringify(payload)); + this.client.send(JSON.stringify(request)); } } @@ -148,5 +196,3 @@ class WebSocketProtocol extends BaseProtocolRealtime { super.close(); } } - -module.exports = WebSocketProtocol; diff --git a/src/protocols/abstract/Base.js b/src/protocols/abstract/Base.ts similarity index 65% rename from src/protocols/abstract/Base.js rename to src/protocols/abstract/Base.ts index 258c6082e..d16040eba 100644 --- a/src/protocols/abstract/Base.js +++ b/src/protocols/abstract/Base.ts @@ -1,12 +1,26 @@ 'use strict'; -const KuzzleError = require('../../KuzzleError'); -const { uuidv4 } = require('../../utils/uuidv4'); -const { KuzzleEventEmitter } = require('../../core/KuzzleEventEmitter'); -const PendingRequest = require('./PendingRequest'); - -class KuzzleAbstractProtocol extends KuzzleEventEmitter { - constructor (host, options = {}, name = undefined) { +import { KuzzleError } from '../../KuzzleError'; +import { uuidv4 } from '../../utils/uuidv4'; +import { KuzzleEventEmitter } from '../../core/KuzzleEventEmitter'; +import { PendingRequest } from './PendingRequest'; +import { KuzzleRequest, JSONObject } from '../../utils/interfaces'; + +/** + * @todo proper TS conversion + */ +export abstract class KuzzleAbstractProtocol extends KuzzleEventEmitter { + private _pendingRequests: Map; + private _host: string; + private _name: string; + private _port: number; + private _ssl: boolean; + + public id: string; + + public state: string; + + constructor (host: string, options: JSONObject = {}, name: string = undefined) { super(); this._pendingRequests = new Map(); @@ -14,7 +28,23 @@ class KuzzleAbstractProtocol extends KuzzleEventEmitter { this._name = name; const port = parseInt(options.port, 10); this._port = isNaN(port) ? 7512 : port; - this._ssl = typeof options.sslConnection === 'boolean' ? options.sslConnection : false; + + if (options.ssl !== undefined && options.sslConnection !== undefined) { + throw new Error('Both "ssl" and "sslConnection" options are set. Use only "ssl".'); + } + + if (typeof options.ssl === 'boolean') { + this._ssl = options.ssl; + } + else if (typeof options.sslConnection === 'boolean') { + this._ssl = options.sslConnection; + } + else if (port === 443 || port === 7443) { + this._ssl = true; + } + else { + this._ssl = false; + } this.id = uuidv4(); this.state = 'offline'; @@ -52,27 +82,14 @@ class KuzzleAbstractProtocol extends KuzzleEventEmitter { return this._pendingRequests; } - /** - * @abstract - * @returns {Promise} - */ - connect () { - throw new Error('Method "connect" is not implemented'); - } + abstract connect (): Promise - /** - * @abstract - * @param request - * @returns {Promise} - */ - send () { - throw new Error('Method "send" is not implemented'); - } + abstract send (request: KuzzleRequest, options: JSONObject): void /** * Called when the client's connection is established */ - clientConnected (state, wasConnected) { + clientConnected (state?: string, wasConnected?: boolean) { this.state = state || 'ready'; this.emit(wasConnected && 'reconnect' || 'connect'); } @@ -136,7 +153,4 @@ Discarded request: ${JSON.stringify(request)}`)); this._pendingRequests.clear(); } - } - -module.exports = { KuzzleAbstractProtocol }; diff --git a/src/protocols/abstract/PendingRequest.js b/src/protocols/abstract/PendingRequest.js index 852ea9a0b..73aa5dfbd 100644 --- a/src/protocols/abstract/PendingRequest.js +++ b/src/protocols/abstract/PendingRequest.js @@ -20,4 +20,4 @@ class PendingRequest { } } -module.exports = PendingRequest; +module.exports = { PendingRequest }; diff --git a/src/protocols/abstract/Realtime.js b/src/protocols/abstract/Realtime.ts similarity index 72% rename from src/protocols/abstract/Realtime.js rename to src/protocols/abstract/Realtime.ts index 0dab94019..74bb34cc0 100644 --- a/src/protocols/abstract/Realtime.js +++ b/src/protocols/abstract/Realtime.ts @@ -1,10 +1,16 @@ 'use strict'; -const { KuzzleAbstractProtocol } = require('./Base'); +import { KuzzleAbstractProtocol } from './Base'; -class BaseProtocolRealtime extends KuzzleAbstractProtocol { - constructor (host, options = {}) { - super(host, options); +export abstract class BaseProtocolRealtime extends KuzzleAbstractProtocol { + protected _autoReconnect: boolean; + protected _reconnectionDelay: number; + protected wasConnected: boolean; + protected stopRetryingToConnect: boolean; + protected retrying: boolean; + + constructor (host, options: any = {}, name: string) { + super(host, options, name); this._autoReconnect = typeof options.autoReconnect === 'boolean' ? options.autoReconnect : true; this._reconnectionDelay = typeof options.reconnectionDelay === 'number' ? options.reconnectionDelay : 1000; @@ -18,18 +24,23 @@ class BaseProtocolRealtime extends KuzzleAbstractProtocol { return this._autoReconnect; } - get reconnectionDelay () { + /** + * Number of milliseconds between reconnection attempts + */ + get reconnectionDelay (): number { return this._reconnectionDelay; } - connect() { + connect (): Promise { this.state = 'connecting'; + + return Promise.resolve(); } /** * Called when the client's connection is established */ - clientConnected() { + clientConnected () { super.clientConnected('connected', this.wasConnected); this.state = 'connected'; @@ -40,7 +51,7 @@ class BaseProtocolRealtime extends KuzzleAbstractProtocol { /** * Called when the client's connection is closed */ - clientDisconnected() { + clientDisconnected () { this.clear(); this.emit('disconnect'); } @@ -54,8 +65,7 @@ class BaseProtocolRealtime extends KuzzleAbstractProtocol { this.state = 'offline'; this.clear(); - const connectionError = new Error(`Unable to connect to kuzzle server at ${this.host}:${this.port}`); - connectionError.internal = error; + const connectionError = new Error(`Unable to connect to kuzzle server at ${this.host}:${this.port}: ${error.message} (ws status=${error.status})`); this.emit('networkError', connectionError); @@ -90,5 +100,3 @@ class BaseProtocolRealtime extends KuzzleAbstractProtocol { return this.state === 'connected'; } } - -module.exports = BaseProtocolRealtime; diff --git a/src/protocols/index.js b/src/protocols/index.js deleted file mode 100644 index 1e39ea546..000000000 --- a/src/protocols/index.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -const Http = require('./Http'); -const WebSocket = require('./WebSocket'); - -module.exports = { - Http, - WebSocket -}; diff --git a/src/protocols/index.ts b/src/protocols/index.ts new file mode 100644 index 000000000..51c13cd4a --- /dev/null +++ b/src/protocols/index.ts @@ -0,0 +1,2 @@ +export { default as WebSocket } from './WebSocket'; +export { default as Http } from './Http'; diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index fe4cc1f89..075464398 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -197,3 +197,40 @@ export interface DocumentHit extends Document { */ _score: number; } + +/** + * HTTP routes definition format + * @example + * { + * : { + * : { , } + * } + * } + * + * { + * 'my-plugin/my-controller': { + * action: { verb: 'GET', url: '/some/url' }, + * action2: { verb: 'GET', url: '/some/url/with/:parameter' } + * } + * } + */ +export interface HttpRoutes { + /** + * Controller name + */ + [key: string]: { + /** + * Action name + */ + [key: string]: { + /** + * HTTP verb + */ + verb: string, + /** + * URL + */ + url: string + } + } +} diff --git a/test/protocol/Base.test.js b/test/protocol/Base.test.js index bd2652459..4955524a7 100644 --- a/test/protocol/Base.test.js +++ b/test/protocol/Base.test.js @@ -1,9 +1,9 @@ -const - should = require('should'), - sinon = require('sinon'), - KuzzleError = require('../../src/KuzzleError'), - { KuzzleAbstractProtocol } = require('../../src/protocols/abstract/Base'), - PendingRequest = require('../../src/protocols/abstract/PendingRequest'); +const should = require('should'); +const sinon = require('sinon'); + +const { KuzzleError } = require('../../src/KuzzleError'); +const { KuzzleAbstractProtocol } = require('../../src/protocols/abstract/Base'); +const { PendingRequest } = require('../../src/protocols/abstract/PendingRequest'); describe('Common Protocol', () => { let @@ -25,6 +25,30 @@ describe('Common Protocol', () => { should(protocol.port).be.eql(443); }); + it('should use ssl option if available and fallback to sslConnection option', () => { + protocol = new KuzzleAbstractProtocol('somewhere', { ssl: true }); + + should(protocol.ssl).be.true(); + + protocol = new KuzzleAbstractProtocol('somewhere', { sslConnection: true }); + + should(protocol.ssl).be.true(); + }); + + it('should use ssl connection when port is 443 or 7443 and option is not defined', () => { + protocol = new KuzzleAbstractProtocol('somewhere', { port: 443 }); + + should(protocol.ssl).be.true(); + + protocol = new KuzzleAbstractProtocol('somewhere', { port: 7443 }); + + should(protocol.ssl).be.true(); + + protocol = new KuzzleAbstractProtocol('somewhere', { port: 4242 }); + + should(protocol.ssl).be.false(); + }); + it('should use 7512 when no port is given or when port is not a parseable number', () => { protocol = new KuzzleAbstractProtocol('somewhere', { port: 'foobar' }); @@ -91,7 +115,7 @@ describe('Common Protocol', () => { const pending = protocol.pendingRequests.get('bar'); - pending.should.be.an.instanceOf(PendingRequest).and.match({request}); + should(pending).be.instanceOf(PendingRequest).and.match({request}); }); it('should fire a "queryError" event and reject if an error occurred', () => { diff --git a/test/protocol/Http.test.js b/test/protocol/Http.test.js index d435a1b1b..18e122f08 100644 --- a/test/protocol/Http.test.js +++ b/test/protocol/Http.test.js @@ -3,7 +3,7 @@ const should = require('should'); const sinon = require('sinon'); const staticHttpRoutes = require('../../src/protocols/routes.json'); -const Http = require('../../src/protocols/Http'); +const { default: Http } = require('../../src/protocols/Http'); describe('HTTP networking module', () => { let protocol; @@ -557,7 +557,7 @@ describe('HTTP networking module', () => { beforeEach(() => { httpRequestStub = sinon.stub().resolves({body: JSON.stringify(mockResponseBody)}); - const MockHttp = proxyquire('../../src/protocols/Http', { + const { default: MockHttp } = proxyquire('../../src/protocols/Http', { 'min-req-promise': {request: httpRequestStub} }); @@ -663,9 +663,7 @@ describe('HTTP networking module', () => { return xhrStub; }; - protocol = new Http('address', { - port: 1234 - }); + protocol = new Http('address', { port: 1234 }); }); afterEach(() => { @@ -829,13 +827,14 @@ describe('HTTP networking module', () => { } }, }; - - protocol.customRoutes = { + const customRoutes = { foo: { list: { verb: 'GET', url: '/overwrite/me/master' } } }; + protocol = new Http('address', { port: 1234, customRoutes }); + const routes = protocol._constructRoutes(publicApi); should(routes.foo.list.url).be.eql('/overwrite/me/master'); diff --git a/test/protocol/WebSocket.test.js b/test/protocol/WebSocket.test.js index cfd3d0989..205f0d443 100644 --- a/test/protocol/WebSocket.test.js +++ b/test/protocol/WebSocket.test.js @@ -1,10 +1,10 @@ -const - should = require('should'), - sinon = require('sinon'), - lolex = require('lolex'), - NodeWS = require('ws'), - WS = require('../../src/protocols/WebSocket'), - windowMock = require('../mocks/window.mock'); +const should = require('should'); +const sinon = require('sinon'); +const lolex = require('lolex'); +const NodeWS = require('ws'); + +const { default: WS } = require('../../src/protocols/WebSocket'); +const windowMock = require('../mocks/window.mock'); describe('WebSocket networking module', () => { let @@ -246,8 +246,6 @@ describe('WebSocket networking module', () => { clock.tick(10); should(cb).be.calledOnce(); should(cb.firstCall.args[0]).be.an.instanceOf(Error); - should(cb.firstCall.args[0].internal.status).be.equal(4666); - should(cb.firstCall.args[0].internal.message).be.equal('foobar'); should(websocket.listeners('networkError').length).be.eql(1); websocket.clear.should.be.calledOnce(); @@ -259,8 +257,6 @@ describe('WebSocket networking module', () => { clock.tick(10); should(cb).be.calledOnce(); should(cb.firstCall.args[0]).be.an.instanceOf(Error); - should(cb.firstCall.args[0].internal.status).be.equal(4666); - should(cb.firstCall.args[0].internal.message).be.equal('foobar'); should(websocket.listeners('networkError').length).be.eql(1); websocket.clear.should.be.calledOnce(); }); diff --git a/tsconfig.json b/tsconfig.json index 1ea049850..4af52bb73 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,8 @@ "moduleResolution": "node", "sourceMap": true, "baseUrl": ".", - "resolveJsonModule": true + "resolveJsonModule": true, + "esModuleInterop": true }, "rootDir": "src/", "include": [