Authors:
Overview
In the concluding article of our series - Building a proprietary web application with LSEG Workspace SDK: An Options RIC Search use case, we will enhance the capabilities of our web application through the seamless integration of the Workspace SDK. In this article we will show how to initialise and use the SDK, open and/or broadcast LSEG Workspace applications. Additionally, we will present how to open a pricing stream and dynamically show it in our Data table. This article will serve as a guide with all the necessary information to successfully integrate WSDK into your application.
Section 1. Workspace SDK Setup and Initialization
Installing Workspace SDK
The Workspace SDK toolset provides APIs and plugins that enable the seamless integration of web-based applications and LSEG Workspace web. The interactions include context exchange navigation and data retrieval. More details about the SDK, including tutorials and documentation can be found in Developer Portal. In this section, we will show how to install and initialize the SDK.
Workspace SDK is published on NPM and to install it we need to use:
npm install --save @refinitiv-workspace-sdk/loader
Additionally, we need Typings library to define session types and modes which is installed automatically as a dependency of the SDK loader. Below we import the packages as the following:
import * as WSDKLoader from '@refinitiv-workspace-sdk/loader';
import { Modes, SxSSessionType } from '@refinitiv-types/workspace-sdk-core';
Initializing the Workspace SDK
Now we have the required libraries installed and loaded, we can proceed with the initialization of the Workspace SDK. The following code snippet illustrates the initiation process using the WSDKLoader.init() method:
WSDKLoader.init(
{
Mode: 'sxs' as Modes.SxS,
SignOnMode: { type: SignOnModeType.AAA },
SxSSession: {
ApiKey: creds.sessions.SxSSession.ApiKey,
ProductId: creds.sessions.SxSSession.ProductId,
Type: 'sxsweb' as SxSSessionType.SxSWeb,
},
},
["DesktopAgent", {
name: 'DataLibrary',
appKey: creds.sessions.SxSSession.ApiKey,
}],
);
}
To initialize the Workspace SDK we need to provide several components, including the connection Mode, Session type with the credentials and the Library plugin. WSDK can be initialized in two modes:
Container – when we need to launch an app inside the LSEG Workspace
Side-by-side – when we want to launch our app as a standalone application where it can interact with both Workspace Web and Desktop apps.
In the scope of our application we initialize the application using Side by Side mode (Mode: 'sxs' as Modes.SxS). More about using Container mode can be found in the official documentation.
The next mandatory component of the initialization of the WSDK is the SxSSession, where we need to provide the authorization details and the session type. For the authorization we need an APP Key and a Product ID. Those can be obtained when registering an APP in APP Key Generator application available in LSEG Workspace. By default, Workspace SDK connects to Workspace Web (SxSSessionType.SxSWeb) which we are using in our application as well. To use the Desktop connectivity instead we need to provide SxSSessionType.SxSDesktop as a session type. In addition to providing the authorication details above, Workspace SDK requires to sign into Workspace. There are two sign on options:
AAA – the default sign on mode, where LSEG Workspace sign in page pops up for user login password input.
SSO – when the SDK will be initialized in Single Sign on mode using the credentials from the web application. To learn and set up the session in this mode please refer to our official documentation.
Finally, in our init() function, we provide the plugin library details, which is loaded during the initialization. There are two plugins available in WSKD: DataLibrary to allow retrieving data via Workspace session and DesktopAgent for launching applications and provide context sharing between opened applications.
The init() function above initializes the Workspace SDK, however, ready() function needs to be called on that to provide the actual application bootstrap logic. This function returns a promise that resolves once the Refinitiv Workspace SDK is fully loaded and ready for interaction.
public getWsdk() {
return WSDKLoader.ready();
}
Finally, we define the initialize_wsdk function which encapsulates the sequence of actions required to fully initialize and integrate the LSEG Workspace SDK into our web application:
async initialize_wsdk() {
const wsdk = await this.getWsdk();
await wsdk.DesktopAgent.joinChannel("1");
wsdk.DesktopAgent.addContextListener(null, (context) => {
console.log(context);
});
}
In this function, utilizing the previously introduced getWsdk() method, we await the readiness of the SDK, ensuring that subsequent actions occur in a synchronized manner. Then we connect a specific channel and incorporate a context listener using addContextListener(), which are important for sharing context between our app and the LSEG Workspace. This will ensure broadcasting context, e.g RICs while operating with LSEG Workspace apps, more on which to be found in the upcoming section.
Section 2. Application Integration and Broadcasting
In this section we will show how to open and/or broadcast LSEG Workspace Apps from our application.
In Workspace SDK, navigation is performed using DesktopAgent open() or broadcast() methods. open() method opens a new application every time the function is called, whereas broadcast () doesn’t open a new application but rather looks for already opened app and sends the context (e.g RIC) to it. This updates the application view with the new context, for example shows quote of another requested RIC in opened Quotes app.
To use these methods, we need to provide App name, launch option and a context object as an input.
Opening a new LSEG Workspace App
Below, we define the openApp function, which uses the DesktopAgent open() to open an app from LSEG Wowkspace:
async openApp(appName: string, ric: string, launchtype: any) {
console.log(`openApp ${appName} for ${ric}`);
const wsdk = await this.getWsdk();
await wsdk.DesktopAgent.open(
{ name: appName, wsdkOptions: { target: launchtype } },
{
type: "fdc3.instrument",
id: {
RIC: ric,
},
},
);
this.appIsAlreadyOpened = true;
this.openedAppName = appName
}
Leveraging the previously acquired SDK instance, in openApp function, we utilize the wsdk.DesktopAgent.open() method. This method orchestrates the opening of a LSEG Workspace application with specified parameters, including the application name (appName) and launch options (wsdkOptions).
With this method we can navigate to any application Workspace can open. To do so, we need to refer to the application via its short code. Application short code can be found in autosuggest when opening an application in LSEG Workspace, e.g OV (Overview), CHT (Chart), Q (Quotes), OPQ (Options Pricer).
As it comes to the launch type, we use ‘tab’ (opens in new tab) and ‘popup’ (opens in pop up window) types in our application. The complete list of available tabs can be found in the official documentation.
In addition to providing app name and the launch type to wsdk.DesktopAgent.open() method, we also pass a context object with the instrument RIC to ensure the requested app is opened for the provided instrument.
Finally, following the successful opening of the application, we update the application status variables, signalling that the application is now open. This information is important for managing the state of our web application and update the context instead of opening a new app when the same app is requested for a different instrument.
Broadcasting context to the opened app
To do that we implement a broadcast() function, which accepts the instrument RIC and instead of using wsdk.DesktopAgent.open() method uses wsdk.DesktopAgent.broascast() to update the app with the new context.
async broadcast(ric: string) {
console.log(`broadcasting ric: ${ric}`);
const wsdk = await this.getWsdk();
await wsdk.DesktopAgent.broadcast({
type: "fdc3.instrument",
id: {
RIC: ric,
},
});
}
Additionally, we provide openAppOrBroadcast() function which checks if the requested app is already open or not and the calls open or broadcast function defined above depending on the state of the app.
async openAppOrBroadcast(appName: string, ric: string, launchtype: any) {
console.log(this.openedAppName, appName)
if (this.appIsAlreadyOpened && this.openedAppName === appName) {
await this.broadcast(ric);
} else {
await this.openApp(appName, ric, launchtype);
}
}
Integrating to the frontend
Throughout our web application, we seamlessly integrate LSEG Workspace apps using the openApp() and openAppOrBroadcast() functions. These functions serve as gateways, allowing to access Overview, Chart, Quotes, and Option Pricer apps from our application.
For example, to show an instrument price chart in LSEG Workspace, we utilize an icon embedded within our DataTable (more to be found in the previous article of the series). By clicking on the chart icon, users can open the Chart app in a pop-up window. Here's a snippet of how this is achieved:
<span title="See instrument price chart in Workspace">
<ef-icon icon="chart-line-bar" id="chartWorkspace" onClick={() => wsdkHelper.openApp('CHT', rowData.ric, 'popup')}></ef-icon>
</span>
The onClick handler triggers the openApp() function, specifying the 'CHT' (Chart) app, the instrument's RIC code (rowData.ric), and the launch type as 'popup'.
Additionally, users might prefer to open or broadcast an app directly by clicking on a cell value in our DataTable. We demonstrate this functionality for Option RICs in the DataTable. The createdCell function is defined to render an interactive link within the DataTable cell:
createdCell: (td: any, cellData: any, rowData: any, row: any, col: any) => {
if (!cellData.includes("^")) {
ReactDOM.render(<a href="#/" id="ric-wsdk" onClick={() => wsdkHelper.openAppOrBroadcast('OPR', cellData, 'tab')}><u>{cellData}</u></a>, td);
}
This snippet creates a hyperlink (<a>) within the DataTable cell, allowing users to click on the Option RIC and either open the 'OPR' (Option Pricer) app in a new tab or broadcast new context (RIC in our case) based on the app's current state.
Section 3. Real-Time Data Streaming
As our integration evolves, we also implement a real-time data retrieval by establishing a streaming session through the DataLibrary plugin of the Workspace SDK. Below is the function getStreamingSession() to implement it:
async getStremingSession(rics: any) {
const wsdk = await this.getWsdk();
console.log('DesktopAgent plugin has loaded: ', !!wsdk.DesktopAgent);
const {Pricing, dataSession} = wsdk.DataLibrary;
try {
await dataSession.open();
const pricingStream = Pricing.Definition({
universe: rics,
fields: ['BID'],
}).getStream();
return pricingStream
}
catch (error: any) {
console.error('Error loading Workspace SDK');
}
}
Prior to setting up and opening the pricing stream, we get the WSDK ready by calling getWSDK function and extract the Pricing and dataSession components from the wsdk.DataLibrary module. These components are integral to managing real-time pricing data within the LSEG Workspace.
Next, we open a data session, establishing a connection to the Data Library and define a pricing stream for a provided RIC and the required fields, in this case, the bid price ('BID').
Finally, we return the created pricing stream, providing our application with a live feed of bid prices for the specified instruments to be populated in the DataTable we have introduced in our previous article of the series.
Integrating to the frontend
Below we incorporate the real-time streaming into our frontend which is an important part of our application. To kickstart the real-time streaming functionality, we initialize the Workspace SDK using the initialize_wsdk() method. This ensures that our application is primed for dynamic interactions with the streaming data:
wsdkHelper.initialize_wsdk()
Utilizing the getStreamingSession() method, we obtain a Pricing Stream for a set of RICs. The Pricing Stream is then configured to respond to updates, status changes, and errors:
(async () => {
let rics = Object.values(res).map(element => element['asset']);
let pricingStream = await wsdkHelper.getStremingSession(rics)
pricingStream
.onUpdate((update, instrument) => {
updateTable(instrument, update.BID)
})
.onStatus((status, instrument) => console.log('logs', 'Status for ' + instrument, status))
.onError(err => console.log('logs', 'PricingStream error:', err));
await pricingStream.open();
})()
In the scope of our application we simply show the status and errors (if any) in the console only and update the the DataTable values if a new item from the feed is received by calling updateTable function below:
async function updateTable(instrument: string, bid: any) {
let rowIndexes: any = [];
await tableRICs.rows(function (idx: any, data: any) {
return data.asset === instrument ? rowIndexes.push(idx) : false;
});
rowIndexes.map((ind: any) => tableRICs.cell({ row: ind, column: 8 }).data(bid).draw(false))
rowIndexes.map((ind: any) => tableRICs.cell({ row: ind, column: 9 }).data(getOptionStatus(tableRICs.row(ind).data(), bid)).draw(false))
}
The updateTable() function translates real-time updates into a visual representation within our DataTable. This function takes the instrument and bid price as parameters, locating the corresponding rows in the DataTable and updates the relevant cell values.
Conclusion
Our exploration of integrating the LSEG Workspace SDK into our web application has uncovered several possibilities for enhancing the functionality of Option RIC Search app. This particularly included a real time price streaming into our app, application integration with the ability to seamlessly open and interact with Workspace applications directly from our web app and the ability of dynamic integration, such as opening applications or broadcasting context depending on the application state.
The following short video demonstrates the capabilities of our application, including those achieved through the Workspace SDK:
References
For further details, please check out the following resources:
- Refinitiv Data Library for Typescript
- Element Framework
- Workspace SDK
- TypeScript programming language: Documentation.
- Developer Article: Functions to find Option RICs traded on different exchanges
For any questions related to this article or the used APIs, please use the Developer Community Q&A Forum.
- Register or Log in to applaud this article
- Let the author know how much this article helped you