@slack/socket-mode
Advanced tools
Comparing version 1.3.0-rc.0 to 1.3.0-rc.1
@@ -32,3 +32,4 @@ import { EventEmitter } from 'eventemitter3'; | ||
/** | ||
* Begin a Socket Mode session. | ||
* Start a Socket Mode session app. | ||
* It may take a few milliseconds before being connected. | ||
* This method must be called before any messages can be sent or received. | ||
@@ -38,2 +39,8 @@ */ | ||
/** | ||
* Start a Socket Mode session asynchronously. | ||
* It may take a few milliseconds before being connected. | ||
* This method must be called before any messages can be sent or received. | ||
*/ | ||
connect(): Promise<void>; | ||
/** | ||
* End a Socket Mode session. After this method is called no messages will be sent or received | ||
@@ -96,2 +103,6 @@ * unless you call start() again later. | ||
/** | ||
* This flag can be true when this client is switching to a new connection. | ||
*/ | ||
private isSwitchingConnection; | ||
/** | ||
* WebClient options we pass to our WebClient instance | ||
@@ -113,4 +124,8 @@ * We also reuse agent and tls for our WebSocket connection | ||
private handleConnectionFailure; | ||
private handleDisconnection; | ||
private markCurrentWebSocketAsInactive; | ||
/** | ||
* Clean up all the remaining connections. | ||
*/ | ||
private terminateAllConnections; | ||
/** | ||
* Set up method for the client's WebSocket instance. This method will attach event listeners. | ||
@@ -122,8 +137,12 @@ */ | ||
*/ | ||
private tearDownHeartBeatJobs; | ||
private terminateActiveHeartBeatJobs; | ||
/** | ||
* Switch the active connection to the secondary if exists. | ||
*/ | ||
private switchWebSocketConnection; | ||
/** | ||
* Tear down method for the client's WebSocket instance. | ||
* This method undoes the work in setupWebSocket(url). | ||
*/ | ||
private tearDownWebSocket; | ||
private terminateWebSocketSafely; | ||
private startPeriodicallySendingPingToSlack; | ||
@@ -130,0 +149,0 @@ private handlePingPongErrorReconnection; |
@@ -88,7 +88,4 @@ "use strict"; | ||
.onEnter(() => { | ||
if (this.badConnection) { | ||
// The state arrived here because Event.ServerPingTimeout occurred | ||
// and a new connection was created. | ||
// Tearing down the old connection. | ||
this.tearDownWebSocket(); | ||
if (this.isSwitchingConnection) { | ||
this.switchWebSocketConnection(); | ||
this.badConnection = false; | ||
@@ -128,3 +125,3 @@ } | ||
.onEnter(() => { | ||
this.logger.info('Going to establish a new connectiont to Slack ...'); | ||
this.logger.info('Going to establish a new connection to Slack ...'); | ||
}) | ||
@@ -136,7 +133,7 @@ .submachine(this.connectingStateMachineConfig) | ||
.transitionTo(State.Reconnecting).withCondition(this.autoReconnectCondition.bind(this)) | ||
.transitionTo(State.Disconnected).withAction(this.handleDisconnection.bind(this)) | ||
.transitionTo(State.Disconnecting) | ||
.on(Event.ExplicitDisconnect) | ||
.transitionTo(State.Disconnecting) | ||
.on(Event.Failure) | ||
.transitionTo(State.Disconnected) | ||
.on(Event.ExplicitDisconnect) | ||
.transitionTo(State.Disconnecting) | ||
.state(State.Connected) | ||
@@ -148,34 +145,25 @@ .onEnter(() => { | ||
.submachine(this.connectedStateMachineConfig) | ||
.on(Event.ServerDisconnectWarning) | ||
.transitionTo(State.Reconnecting).withCondition(this.autoReconnectCondition.bind(this)) | ||
.on(Event.WebSocketClose) | ||
.transitionTo(State.Reconnecting).withCondition(this.autoReconnectCondition.bind(this)) | ||
.transitionTo(State.Disconnected).withAction(this.handleDisconnection.bind(this)) | ||
.transitionTo(State.Reconnecting) | ||
.withCondition(this.autoReconnectCondition.bind(this)) | ||
.withAction(() => this.markCurrentWebSocketAsInactive()) | ||
.transitionTo(State.Disconnecting) | ||
.on(Event.ExplicitDisconnect) | ||
.transitionTo(State.Disconnecting) | ||
.on(Event.ServerDisconnectWarning) | ||
.transitionTo(State.Reconnecting).withCondition(this.autoReconnectCondition.bind(this)) | ||
.withAction(() => this.markCurrentWebSocketAsInactive()) | ||
.on(Event.ServerPingsNotReceived) | ||
.transitionTo(State.Reconnecting).withCondition(this.autoReconnectCondition.bind(this)) | ||
.transitionTo(State.Disconnecting) | ||
.on(Event.ServerPongsNotReceived) | ||
.transitionTo(State.Reconnecting).withCondition(this.autoReconnectCondition.bind(this)) | ||
.transitionTo(State.Disconnecting) | ||
.on(Event.ServerDisconnectWarning) | ||
.transitionTo(State.Reconnecting).withCondition(this.autoReconnectCondition.bind(this)) | ||
.transitionTo(State.Disconnecting) | ||
.on(Event.ServerDisconnectOldSocket) | ||
.transitionTo(State.Reconnecting).withCondition(this.autoReconnectCondition.bind(this)) | ||
.transitionTo(State.Disconnecting) | ||
.onExit(() => { | ||
this.connected = false; | ||
this.authenticated = false; | ||
this.tearDownHeartBeatJobs(); | ||
this.terminateActiveHeartBeatJobs(); | ||
}) | ||
.state(State.Disconnecting) | ||
.onEnter(() => { | ||
// Most of the time, a WebSocket will exist. | ||
// The only time it does not is when transitioning from connecting, | ||
// before the client.start() has finished and the WebSocket hasn't been set up. | ||
if (this.websocket !== undefined) { | ||
this.websocket.close(); | ||
} | ||
}) | ||
.on(Event.WebSocketClose) | ||
.transitionTo(State.Disconnected) | ||
.onExit(() => this.tearDownWebSocket()) | ||
.state(State.Reconnecting) | ||
@@ -186,6 +174,17 @@ .onEnter(() => { | ||
.do(async () => { | ||
this.badConnection = true; | ||
this.tearDownHeartBeatJobs(); | ||
this.isSwitchingConnection = true; | ||
}) | ||
.onSuccess().transitionTo(State.Connecting) | ||
.onFailure().transitionTo(State.Failed) | ||
.state(State.Disconnecting) | ||
.onEnter(() => { | ||
this.logger.info('Disconnecting ...'); | ||
}) | ||
.do(async () => { | ||
this.terminateActiveHeartBeatJobs(); | ||
this.terminateAllConnections(); | ||
this.logger.info('Disconnected from Slack'); | ||
}) | ||
.onSuccess().transitionTo(State.Disconnected) | ||
.onFailure().transitionTo(State.Failed) | ||
.getConfig(); | ||
@@ -196,2 +195,6 @@ /** | ||
this.badConnection = false; | ||
/** | ||
* This flag can be true when this client is switching to a new connection. | ||
*/ | ||
this.isSwitchingConnection = false; | ||
if (appToken === undefined) { | ||
@@ -232,7 +235,8 @@ throw new Error('Must provide an App-Level Token when initializing a Socket Mode Client'); | ||
/** | ||
* Begin a Socket Mode session. | ||
* Start a Socket Mode session app. | ||
* It may take a few milliseconds before being connected. | ||
* This method must be called before any messages can be sent or received. | ||
*/ | ||
start() { | ||
this.logger.debug('Starting a Socket Mode client'); | ||
this.logger.debug('Starting a Socket Mode client ...'); | ||
// Delegate behavior to state machine | ||
@@ -253,2 +257,19 @@ this.stateMachine.handle(Event.Start); | ||
/** | ||
* Start a Socket Mode session asynchronously. | ||
* It may take a few milliseconds before being connected. | ||
* This method must be called before any messages can be sent or received. | ||
*/ | ||
connect() { | ||
return new Promise((resolve, reject) => { | ||
try { | ||
// The state machine handles all the WebSocket releated operations. | ||
this.stateMachine.handle(Event.Start); | ||
resolve(); | ||
} | ||
catch (e) { | ||
reject(e); | ||
} | ||
}); | ||
} | ||
/** | ||
* End a Socket Mode session. After this method is called no messages will be sent or received | ||
@@ -345,11 +366,28 @@ * unless you call start() again later. | ||
handleConnectionFailure(_state, context) { | ||
this.logger.error(`The internal logic unexpectedly failed (error: ${context.error})`); | ||
// Terminate everything, just in case | ||
this.terminateActiveHeartBeatJobs(); | ||
this.terminateAllConnections(); | ||
// dispatch 'failure' on parent machine to transition out of this submachine's states | ||
this.stateMachine.handle(Event.Failure, context.error); | ||
} | ||
handleDisconnection() { | ||
// This transition circumvents the 'disconnecting' state | ||
// (since the websocket is already closed), so we need to execute its onExit behavior here. | ||
this.tearDownWebSocket(); | ||
markCurrentWebSocketAsInactive() { | ||
this.badConnection = true; | ||
this.connected = false; | ||
this.authenticated = false; | ||
} | ||
/** | ||
* Clean up all the remaining connections. | ||
*/ | ||
terminateAllConnections() { | ||
if (this.secondaryWebsocket !== undefined) { | ||
this.terminateWebSocketSafely(this.secondaryWebsocket); | ||
this.secondaryWebsocket = undefined; | ||
} | ||
if (this.websocket !== undefined) { | ||
this.terminateWebSocketSafely(this.websocket); | ||
this.websocket = undefined; | ||
} | ||
} | ||
/** | ||
* Set up method for the client's WebSocket instance. This method will attach event listeners. | ||
@@ -369,8 +407,8 @@ */ | ||
else { | ||
// setup secondary websocket | ||
// this is used when creating a new connection because the first is about to disconnect | ||
// Set up secondary websocket | ||
// This is used when creating a new connection because the first is about to disconnect | ||
this.secondaryWebsocket = new ws_1.default(url, options); | ||
websocket = this.secondaryWebsocket; | ||
} | ||
// attach event listeners | ||
// Attach event listeners | ||
websocket.addEventListener('open', (event) => { | ||
@@ -408,17 +446,20 @@ this.stateMachine.handle(Event.WebSocketOpen, event); | ||
*/ | ||
tearDownHeartBeatJobs() { | ||
terminateActiveHeartBeatJobs() { | ||
if (this.serverPingTimeout !== undefined) { | ||
clearTimeout(this.serverPingTimeout); | ||
this.serverPingTimeout = undefined; | ||
this.logger.debug('Cancelled the job waiting for ping from Slack'); | ||
} | ||
if (this.clientPingTimeout !== undefined) { | ||
clearTimeout(this.clientPingTimeout); | ||
this.clientPingTimeout = undefined; | ||
this.logger.debug('Terminated the heart beat job'); | ||
} | ||
} | ||
/** | ||
* Tear down method for the client's WebSocket instance. | ||
* This method undoes the work in setupWebSocket(url). | ||
* Switch the active connection to the secondary if exists. | ||
*/ | ||
tearDownWebSocket() { | ||
switchWebSocketConnection() { | ||
if (this.secondaryWebsocket !== undefined && this.websocket !== undefined) { | ||
this.logger.debug('Since the secondary WebSocket exists, going to tear down the first and assign second ...'); | ||
this.logger.debug('Switching to the secondary connection ...'); | ||
// Currently have two WebSocket objects, so tear down the older one | ||
@@ -429,32 +470,27 @@ const oldWebsocket = this.websocket; | ||
this.secondaryWebsocket = undefined; | ||
this.logger.debug('Switched to the secondary connection'); | ||
// Swithcing the connection is done | ||
this.isSwitchingConnection = false; | ||
// Clean up the old one | ||
try { | ||
oldWebsocket.removeAllListeners('open'); | ||
oldWebsocket.removeAllListeners('close'); | ||
oldWebsocket.removeAllListeners('error'); | ||
oldWebsocket.removeAllListeners('message'); | ||
oldWebsocket.close(); | ||
oldWebsocket.terminate(); | ||
} | ||
catch (e) { | ||
this.logger.error(`Failed to terminate the old WS connection (error: ${e})`); | ||
} | ||
this.terminateWebSocketSafely(oldWebsocket); | ||
this.logger.debug('Terminated the old connection'); | ||
} | ||
else if (this.secondaryWebsocket === undefined && this.websocket !== undefined) { | ||
this.logger.debug('Since only the primary WebSocket exists, going to tear it down ...'); | ||
// The only one WebSocket to tear down | ||
} | ||
/** | ||
* Tear down method for the client's WebSocket instance. | ||
* This method undoes the work in setupWebSocket(url). | ||
*/ | ||
terminateWebSocketSafely(websocket) { | ||
if (websocket !== undefined) { | ||
try { | ||
this.websocket.removeAllListeners('open'); | ||
this.websocket.removeAllListeners('close'); | ||
this.websocket.removeAllListeners('error'); | ||
this.websocket.removeAllListeners('message'); | ||
this.websocket.close(); | ||
this.websocket.terminate(); | ||
websocket.removeAllListeners('open'); | ||
websocket.removeAllListeners('close'); | ||
websocket.removeAllListeners('error'); | ||
websocket.removeAllListeners('message'); | ||
websocket.terminate(); | ||
} | ||
catch (e) { | ||
this.logger.error(`Failed to terminate the old WS connection (error: ${e})`); | ||
this.logger.error(`Failed to terminate a connection (error: ${e})`); | ||
} | ||
this.websocket = undefined; | ||
} | ||
this.logger.debug('Tearing down the old WebSocket connection has finished'); | ||
} | ||
@@ -500,2 +536,3 @@ startPeriodicallySendingPingToSlack() { | ||
}, this.clientPingTimeoutMillis / 3); | ||
this.logger.debug('Started running a new heart beat job'); | ||
} | ||
@@ -505,2 +542,3 @@ } | ||
try { | ||
this.badConnection = true; | ||
this.stateMachine.handle(Event.ServerPongsNotReceived); | ||
@@ -572,4 +610,2 @@ } | ||
this.stateMachine.handle(Event.ServerDisconnectOldSocket); | ||
// TODO: instead of using this event to reassign secondaryWebsocket to this.websocket, | ||
// use the WebSocket close event | ||
return; | ||
@@ -576,0 +612,0 @@ } |
{ | ||
"name": "@slack/socket-mode", | ||
"version": "1.3.0-rc.0", | ||
"version": "1.3.0-rc.1", | ||
"description": "Official library for using the Slack Platform's Socket Mode API", | ||
@@ -5,0 +5,0 @@ "author": "Slack Technologies, LLC", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
83076
1016