Commit 1f42d0b0 authored by Djorkaeff Alexandre's avatar Djorkaeff Alexandre
Browse files

[IMPROVEMENT] Injectable Timer (DIP)

parent 0a97c818
......@@ -7,6 +7,14 @@ export interface ILogger {
error: (...args: any[]) => void
}
/** Timer need to provide the same set of methods */
export interface ITimer {
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timeout | number;
clearTimeout(timeoutId: NodeJS.Timeout | number): void;
setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timeout | number;
clearInterval(intervalId: NodeJS.Timeout | number): void;
}
/**
* Connection options type
* @param host Host URL:PORT, converted to websocket protocol
......
import { ISocket, IDriver, Protocols } from '../drivers'
import ClientRest from '../api/RocketChat'
import { ILogger, ISocketOptions, ICallback, ISubscription, ICredentials } from '../../interfaces'
import { ILogger, ISocketOptions, ICallback, ISubscription, ICredentials, ITimer } from '../../interfaces'
import { logger as Logger } from '../log'
import { timer as Timer } from '../timer'
export default class RocketChatClient extends ClientRest implements ISocket {
userId: string = ''
logger: ILogger = Logger
socket: Promise<ISocket | IDriver>
config: any
constructor ({ logger, allPublic, rooms, integrationId, protocol = Protocols.DDP, ...config }: any) {
constructor ({ logger, timer = Timer, allPublic, rooms, integrationId, protocol = Protocols.DDP, ...config }: any) {
super({ ...config, logger })
this.logger = logger
switch (protocol) {
......@@ -16,7 +17,7 @@ export default class RocketChatClient extends ClientRest implements ISocket {
// this.socket = import(/* webpackChunkName: 'mqtt' */ '../drivers/mqtt').then(({ MQTTDriver }) => new MQTTDriver({ ...config, logger }))
// break
case Protocols.DDP:
this.socket = import(/* webpackChunkName: 'ddp' */ '../drivers/ddp').then(({ DDPDriver }) => new DDPDriver({ ...config, logger }))
this.socket = import(/* webpackChunkName: 'ddp' */ '../drivers/ddp').then(({ DDPDriver }) => new DDPDriver({ ...config, logger, timer }))
break
default:
throw new Error(`Invalid Protocol: ${protocol}, valids: ${Object.keys(Protocols).join()}`)
......
......@@ -7,6 +7,7 @@ import WebSocket from 'universal-websocket-client'
import { EventEmitter } from 'tiny-events'
import { logger as Logger } from '../log'
import { timer as Timer } from '../timer'
import { ISocket, IDriver } from './index'
import * as settings from '../settings';
......@@ -34,7 +35,8 @@ import {
isLoginResult,
ISocketMessageCallback,
ICallback,
ILogger
ILogger,
ITimer
} from '../../interfaces'
import { hostToWS } from '../util'
......@@ -54,6 +56,7 @@ export class Socket extends EventEmitter {
connection?: WebSocket
session?: string
logger: ILogger
timer: ITimer
/** Create a websocket handler */
constructor (
......@@ -62,6 +65,7 @@ export class Socket extends EventEmitter {
) {
super()
this.logger = options.logger || Logger
this.timer = options.timer || Timer
this.config = {
host: options.host || 'http://localhost:3000',
useSsl: options.useSsl || false,
......@@ -88,8 +92,8 @@ export class Socket extends EventEmitter {
return new Promise(async (resolve, reject) => {
let connection: WebSocket
this.reopenInterval && clearInterval(this.reopenInterval as any)
this.reopenInterval = setInterval(() => {
this.reopenInterval && this.timer.clearInterval(this.reopenInterval as any)
this.reopenInterval = this.timer.setInterval(() => {
return !this.alive() && this.reopen()
}, ms)
......@@ -159,9 +163,9 @@ export class Socket extends EventEmitter {
close = async () => {
this.unsubscribeAll().catch(e => this.logger.debug(e))
this.reopenInterval && clearInterval(this.reopenInterval as any)
this.openTimeout && clearTimeout(this.openTimeout as any)
this.pingTimeout && clearTimeout(this.pingTimeout as any)
this.reopenInterval && this.timer.clearInterval(this.reopenInterval as any)
this.openTimeout && this.timer.clearTimeout(this.openTimeout as any)
this.pingTimeout && this.timer.clearTimeout(this.pingTimeout as any)
if (this.connected) {
await new Promise((resolve) => {
......@@ -179,7 +183,7 @@ export class Socket extends EventEmitter {
/** Clear connection and try to connect again. */
reopen = async () => {
if (this.openTimeout) return
this.openTimeout = setTimeout(() => { delete this.openTimeout }, this.config.reopen);
this.openTimeout = this.timer.setTimeout(() => { delete this.openTimeout }, this.config.reopen);
await this.open()
.catch((err) => {
......@@ -249,8 +253,8 @@ export class Socket extends EventEmitter {
/** Send ping, record time, re-open if nothing comes back, repeat */
ping = async () => {
this.pingTimeout && clearTimeout(this.pingTimeout as any)
this.pingTimeout = setTimeout(() => {
this.pingTimeout && this.timer.clearTimeout(this.pingTimeout as any)
this.pingTimeout = this.timer.setTimeout(() => {
this.send({ msg: 'ping' })
.then(() => this.ping())
.catch(() => this.reopen())
......@@ -395,6 +399,7 @@ export class Socket extends EventEmitter {
export class DDPDriver extends EventEmitter implements ISocket, IDriver {
logger: ILogger
timer: ITimer
config: ISocketOptions
/**
* Event Emitter for listening to connection (echoes selection of DDP events)
......@@ -427,7 +432,7 @@ export class DDPDriver extends EventEmitter implements ISocket, IDriver {
/** Array of joined room IDs (for reactive queries) */
joinedIds: string[] = []
constructor ({ host = 'localhost:3000', integrationId, config, logger = Logger, ...moreConfigs }: any = {}) {
constructor ({ host = 'localhost:3000', integrationId, config, logger = Logger, timer = Timer, ...moreConfigs }: any = {}) {
super()
this.config = {
......@@ -440,8 +445,9 @@ export class DDPDriver extends EventEmitter implements ISocket, IDriver {
// close: number
// integration: string
}
this.ddp = new Socket({ ...this.config, logger })
this.ddp = new Socket({ ...this.config, logger, timer })
this.logger = logger
this.timer = timer
}
/**
......@@ -472,7 +478,7 @@ export class DDPDriver extends EventEmitter implements ISocket, IDriver {
this.ddp.on('open', () => this.emit('connected')) // echo ddp event
let cancelled = false
const rejectionTimeout = setTimeout(() => {
const rejectionTimeout = this.timer.setTimeout(() => {
this.logger.info(`[driver] Timeout (${config.timeout})`)
const err = new Error('Socket connection timeout')
cancelled = true
......@@ -485,7 +491,7 @@ export class DDPDriver extends EventEmitter implements ISocket, IDriver {
this.once('connected', () => {
this.logger.info('[driver] Connected')
if (cancelled) return this.ddp.close() // cancel if already rejected
clearTimeout(rejectionTimeout)
this.timer.clearTimeout(rejectionTimeout)
resolve(this as IDriver)
})
}
......
/**
* @module timer
* Basic timer handling.
*/
import { ITimer } from '../interfaces'
class InternalTimer implements ITimer {
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): number | NodeJS.Timeout {
return setTimeout(callback, ms, ...args)
}
clearTimeout(timeoutId: number | NodeJS.Timeout): void {
return clearTimeout(timeoutId as NodeJS.Timeout)
}
setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): number | NodeJS.Timeout {
return setInterval(callback, ms, ...args)
}
clearInterval(intervalId: number | NodeJS.Timeout): void {
return clearInterval(intervalId as NodeJS.Timeout)
}
}
/** Default basic timer */
export let timer: ITimer = new InternalTimer()
\ No newline at end of file
{
"name": "@rocket.chat/sdk",
"version": "1.0.0-mobile",
"version": "1.1.0-mobile",
"description": "Node.js SDK for Rocket.Chat. Application interface for server methods and message streams.",
"main": "index.ts",
"types": "index.d.ts",
......
This diff is collapsed.
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment