The subscriptions and messaging APIs in ICWS
provide a way for applications to be informed when changes to the Interaction Center system take place. For more information about the subscription concept, see the Design Principles page.
This topic contains the following sections:
Subscriptions are how client applications express interest in receiving messages about certain changes in the Interaction Center system. Resources with paths starting with /icws/{sessionId}/messaging/subscriptions/...
are provided to manipulate each subscription. Subscriptions are created and modified using the PUT
method and canceled by using the DELETE
method. Every subscription has an associated message type that client applications can expect when that subscription is active.
An example subscription is for the statuses of various users in the Interaction Center system. To create this subscription, the client application must use the PUT /icws/{sessionId}/messaging/subscriptions/status/user-statuses method and provide a list of userIds
that the application is interested in receiving status information for. The following shows an example of how a client application might create a user status subscription for two users whose userIds
are user1
and user2
.
// This example snippet assumes the existence of a helper // method for sending a request to the active ICWS session. var sendRequest = function(httpMethod, path, requestPayload, responseCallback) { }; // The type of the message associated with the user-statuses subscription userStatusMessageType = 'urn:inin.com:status:userStatusMessage' /** * Starts the subscription for users statuses for user1 and user2. * @see stopUserStatusSubscription */ function startUserStatusSubscription() { // The PUT request requires a list of userIds payload = { userIds:['user1', 'user2'] }; session.sendRequest('PUT', '/messaging/subscriptions/status/user-statuses', payload, function(status, jsonResponse) { if ((status >= 200) && (status <= 299)) { // Subscription was started } else { // There was an error starting the subscription. // Details are in the jsonResponse. throw new Error(jsonResponse) } }); } /** * Stops the subscription for users statuses. * @see startUserStatusSubscription */ function stopUserStatusSubscription() { // The DELETE request does not take any payload values. payload = {}; session.sendRequest('DELETE', '/messaging/subscriptions/status/user-statuses', payload, function(status, jsonResponse) { if ((status >= 200) && (status <= 299)) { // Subscription was stopped } else { // There was an error stopping the subscription. // Details are in the jsonResponse. throw new Error(jsonResponse) } }); } function displayUserStatuses(userStatusMessage) { var statusIndex, statusCount, currentStatus; if (!userStatusMessage.isDelta) { // If the message is not a delta, we must clear all previous // information and prepare for refreshed information clearStatusDisplay(); } // process the userStatusMessage to update the display statusCount = userStatusMessage.userStatusList.length; for (statusIndex = 0; statusIndex < statusCount; statusIndex++) { currentStatus = userStatusMessage.userStatusList[statusIndex]; updateStatusForUser(currentStatus.userId, currentStatus.statusId); } } // Register a callback with the message channel (see example in the Retrieving Messages section) // Note: The registration happens before the subscription is started to ensure no messages are missed. registerMessageCallback(userStatusMessageType, displayUserStatuses); // Start the subscription startUserStatusSubscription(); // The subscription can be stopped using the following: stopUserStatusSubscription();
There are two potential ways of receiving event messages in a client application:
With the first method - short-polling - event messages are queued in the messaging channel on the ICWS
server until the client application requests the contents where upon the messages are removed from the queue and sent to the application. The messages are retrieved through the GET /icws/{sessionId}/messaging/messages method. Client applications should periodically poll this resource to obtain the messages.
With the second method - server-sent events - event messages are sent in the messaging channel on the ICWS
server as they become available. In this case, it is not necessary to periodically poll the GET /icws/{sessionId}/messaging/messages resource; however, an appropriate event source object must be instantiated. Prior to utilizing server-sent events, client applications should ensure that this method is supported on the ICWS
server. This can be accomplished by checking the GET /icws/connection/features resource for the messaging
feature. Server sent events are supported for messaging
version 2 and later. Alternatively, a client application can retrieve the supported feature versions from the features
property of the 201 HTTP response of POST /icws/connection if the include
query parameter is supplied that includes the features
value. For more information about handling versioning, see the Versioning page.
At the time of writing, server-sent events are not supported on Internet Explorer 11 (and older) or Microsoft Edge. According to https://dev.windows.com/microsoft-edge/platform/status/serversenteventseventsource, it is under consideration for a future release of Microsoft Edge. When writing client applications, this should be taken into consideration.
The ICWS
server will attempt to queue all messages until the queue becomes full. When messages are no longer able to be enqueued or sent due to a full queue, the session will be terminated by the server. To prevent this situation, it is important for client applications to retrieve messages on a regular basis if using short-polling, or to ensure that a proper event source is setup in case of server-sent events. If using short-polling, it is recommended to poll every second to help ensure messages are received in a timely manner and to help prevent disconnects.
The following provides a simple example of retrieving pending messages from JavaScript.
// This example snippet assumes the existence of a helper // method for sending a request to the active ICWS session. var sendRequest = function(httpMethod, path, requestPayload, responseCallback) { }; // This example snippet assumes the existence of a helper function: // A function determining whether server-sent events messaging is supported by inspecting the messaging feature version (supported in messaging version 2). var isServerSentEventsSupportedOnServer = function() {}; // Dictionary of ICWS message __type ID to the callback (type: icwsMessageCallback) // to invoke when that message is received. var icwsMessageCallbacks = {}; var messageProcessingTimerId; var eventSourceInstance; // Polling interval for retrieving ICWS message queue. var ICWS_MESSAGE_RETRIEVAL_INTERVAL_MS = 1000; /** * Sets the callback for a particular type of ICWS message. * @param {String} messageType The ICWS message type. (ex: urn:inin.com:status:userStatusMessage) * @param {icwsMessageCallback} messageCallback The callback to invoke with the message details. * @throws {Error} The messageCallback was undefined. * @throws {Error} A callback is already registered for the specified messageType. */ function registerMessageCallback(messageType, messageCallback) { if (messageCallback === undefined) { throw new Error('Invalid argument "messageCallback".'); } if (!icwsMessageCallbacks[messageType]) { icwsMessageCallbacks[messageType] = messageCallback; } else { throw new Error('Message callback already registered for message type: ' + messageType); } }; /** * Starts the message processing mechanism, if not already running. * @see stopMessageProcessing */ function startMessageProcessing() { if (isServerSentEventsSupportedOnServer() && typeof EventSource !== 'undefined') { if (!eventSourceInstance || eventSourceInstance.readyState === EventSource.CLOSED) { var messagingUrl = getSessionUrl('/messaging/messages'); eventSourceInstance = new EventSource(messagingUrl, { withCredentials: true // Allows the Cookie HTTP request header to be sent }); eventSourceInstance.onmessage = onServerSentEventMessage; } } else { // Only send the next request once the previous result has been received. function runTimerInstance() { messageProcessingTimerCallback(); messageProcessingTimerId = setTimeout(runTimerInstance, ICWS_MESSAGE_RETRIEVAL_INTERVAL_MS); } if (!messageProcessingTimerId) { runTimerInstance(); } } } /** * Stops the message processing mechanism, if running. * @see startMessageProcessing */ function stopMessageProcessing() { if (eventSourceInstance) { if (eventSourceInstance.readyState !== EventSource.CLOSED) { eventSourceInstance.stop(); } } else if (!!messageProcessingTimerId) { clearTimeout(messageProcessingTimerId); messageProcessingTimerId = null; } } /** * Implements the message processing mechanism timer callback. * @see startMessageProcessing * @see stopMessageProcessing */ function messageProcessingTimerCallback() { var payload, messageIndex, messageCount, jsonMessage, messageType, messageCallback; // The messaging GET request does not take any payload values. payload = {}; sendRequest('GET', '/messaging/messages', payload, function(status, jsonResponse) { if ((status >= 200) && (status <= 299)) { // Process retrieved messages. messageCount = jsonResponse.length; for (messageIndex = 0; messageIndex < messageCount; messageIndex++) { jsonMessage = jsonResponse[messageIndex]; messageType = jsonMessage.__type; // For each message, invoke a registered message callback if there is one. messageCallback = icwsMessageCallbacks[messageType]; if (messageCallback) { messageCallback(jsonMessage); } else { // No registered message handler. } } } }); /** * Handles eventSourceInstance's message event and relays it to the correct callback. * @param {Event} event The onmessage event data. * @see stopMessageProcessing */ function onServerSentEventMessage(event) { var message, messageType, messageCallback; try { // Process the data off of the event. It is a JSON string. var message = JSON.parse(event.data); messageType = message.__type; // Invoke a registered message callback if there is one. messageCallback = icwsMessageCallbacks[messageType]; if (messageCallback) { messageCallback(jsonMessage); } else { // No registered message handler. } } catch (error) { // Failed to process message. } } }