Creating a Desktop Service

Any theatrical performance has actors—the people in front of you, making noise and performing actions. But there are also the stagehands backstage making sure that all the actors get what they need, and generally making sure the production goes smoothly. In Finsemble, desktop services are those stagehands: operating invisibly in the background, providing central infrastructure to the components that need it.

Generating a desktop services template with the CLI

The easiest way to set up your desktop service is to use our command-line interface. If you have the CLI, run the following: finsemble-cli add service <desktopServiceName>

This will create two template files, desktopServiceName.js and desktopServiceName.html, in the folder /src/services/desktopServiceName/. It will also register your desktop service in /configs/application/services.json and ensure that it is built by Webpack.

Accessing Client APIs from Finsemble desktop services

Unlike components, desktop services do not have the FSBL object injected into them, which normally provides you with access to our pre-initialized API clients. Hence, any clients that you need to use in a desktop service must be imported and initialized. For example:

import Finsemble from "@chartiq/finsemble";
const HotKeysClient = Finsemble.Clients.HotkeysClient;
HotkeysClient.initialize();
HotkeysClient.onReady(function(){
});
// It's now safe to use your client objects (e.g., you can use HotkeysClient to register your hotkeys)

The exception to this pattern is the Logger. To import the Logger, use:

import Finsemble from "@chartiq/finsemble";
const Logger = Finsemble.Clients.Logger;
Logger.start();
/// Logger is safe to use immediately

The Base Service

During start-up, Finsemble needs to ensure that the clients and desktop services needed are all available before it tries to start your desktop service. Similarly, Finsemble may need to ensure that your desktop service has access to another (such as the Storage Service) as it shuts down. Note: The Central Logger is a good tool for discovering the names of clients and services utilized by Finsemble.

To orchestrate start-up and shutdown we provide the Base Service prototype which desktop services can extend:

function myService() {
	//service functions
	this.myFunction = function (args, callback) {
		...
	};
	return this;
}
const baseService = Finsemble.baseService;
myService.prototype = new baseService({
	startupDependencies: {
		clients: ["distributedStoreClient"],
		services: ["authenticationService", "routerService"]
	},
	shutdownDependencies: {
		services: ["distributedStoreService", "storageService"]
	}
});

The Base Service constructor takes a single params object with two properties: startupDependencies and shutdownDependencies. Both clients and desktop services can be listed as start-up dependencies but only desktop services can be listed as shutdown dependencies. If a desktop service or client is listed as a start-up dependency, your desktop service will not start until that dependency is available. We recommend that you should depend on the client for a desktop service, rather than the desktop service itself, as the client itself will either depend on the desktop service or support the queuing of requests until it is up.

When all dependencies are satisfied, the onBaseServiceReady event will be emitted and you can begin setting up your desktop service.

let serviceInstance = new myService('myService');
serviceInstance.onBaseServiceReady(function (callback) {
	...
});

Using your desktop service

desktop services can communicate with components and other desktop services through the Router Client. For example:

serviceInstance.onBaseServiceReady(function (callback) {
    ...
	RouterClient.addResponder("YF server", function(error, queryMessage) {
		if (!error) {
			if (queryMessage.data.query === "myFunction") {
				queryMessage.sendQueryResponse(null, serviceInstance.myFunction());
			} else {
				queryMessage.sendQueryResponse("Unknown query function: " + queryMessage, null);
				Logger.error("Unknown query function: ", queryMessage);
			}
		} else {
			Logger.error("Failed to setup query responder", error);
		}
	});

	Logger.log("myService ready");
	callback();
});

A simple client may be created by creating another file in the same directory as your service, e.g., myServiceClient.js:

const Logger = FSBL.Clients.Logger;
const RouterClient =  FSBL.Clients.RouterClient;

export function myFunction(cb) {
    Logger.log("myFunction called");
    RouterClient.query("myService functions", { query: "myFunction" }, function (err, response) {
        Logger.log("myService.myFunction response: ", response.data);
        if (cb) {
            cb(err, response.data);
        }
    });
};

You can then import or require in any component that will need to access the desktop service, e.g.:

import {myFunction} from '../../services/myServiceClient';

Then, you can use it (asynchronously) in the component as if it were a local function:

myFunction(function(err, response) {
    if (err) {
        Logger.error("Failed to call myFunction!", err);
    } else {
        Logger.log("called myFunction: ", response);
    }
});

check   The CLI makes it easy to create new desktop services. Any clients that you need to use in a desktop service must be imported and initialized.
 

Further reading

The Base Service is used to create a new desktop service.

Finsemble desktop services are communicated with via the Router.