Configuration

Finsemble's configuration is pulled from JSON files on start-up, put in an easily accessible form, and then made available to all components and services for reference.

This configuration can be partitioned into two main categories: Finsemble's core configuration and Finsemble's application-level configuration. Typically, when developing Finsemble applications, a developer only needs the application-level configuration, which is contained in configs/application/config.json. However, it helps to understand Finsemble's complete configuration sequence and the recommended Configuration API.

OpenFin starts Finsemble as directed by the OpenFin manifest file located in your repo at configs/openfin/manifest-local.json. Immediately after start-up, Finsemble spawns the Config Service to dynamically assemble the full Finsemble configuration from three sources using the following steps:

  • Step 1: Pull in the Finsemble properties that are defined within the OpenFin manifest file (you can see the finsemble property at the bottom of the manifest file). This provides enough Finsemble configuration to bootstrap the rest of Finsemble and assemble the complete configuration.
  • Step 2: Pull in the Finsemble core configuration from internal JSON files. These files are stored either on a server or within a development build directory (e.g., "dist") under ..finsemble/configs/core.
  • Step 3: Pull in the application-level config, stored in your repository under ..configs/application/config.json.

After the Finsemble configuration has been assembled by these steps, the Finsemble start-up sequence continues by launching services and components.

After start-up, the Config Service is available to respond to configuration queries from any service or component. Generally, no service or components should ever read config settings directly from JSON files. Instead, config settings should be retrieved using the ConfigClient, which interfaces with the Config Service.

The Config Service is started early, so it is always available to other components and services (again, through the Config Client API).


Finsemble configuration variables

In order to simplify JSON configuration settings across multiple repositories and files, Finsemble supports configuration variables. This means any configuration property defined directly under finsemble can be referenced elsewhere in the configuration using the special character "$". For example, myConfigValue can defined under finsemble (i.e., finsemble.myConfigValue) then referenced elsewhere throughout config as$myConfigValue.

Let's look at more complete examples. The following JSON snippet of the finsemble property was pulled out of the seed project's OpenFin manifest file.

    "finsemble": {
        "applicationRoot": "http://localhost:3375/yourSubDirectory/dist",  <------------------------------- applicationRoot defined here
        "moduleRoot": "http://localhost:3375/yourSubDirectory/node_modules/@chartiq/finsemble/dist",  <------------------------------- moduleRoot defined here
        "importConfig": [
            "$applicationRoot/configs/application/config.json"  <------------------------------- $applicationRoot referenced here
        ]
    }

The applicationRoot property, as shown above, defines the path for all application files (after the files are built using Webpack and copied to the dist directory). $applicationRoot is then referenced just a few lines down under the importConfig property to pull in the application-level configuration. Likewise, the moduleRoot property defines the path for all components located in the Finsemble NPM module.

Also, as shown below, the same $applicationRoot is referenced in configs/application/components.json to set the URL property of a component.

    "components": {
        "accountDetail": {
            "window": {
                "url": "$applicationRoot/components/accountDetail/accountDetail.html", <------------------------------- $applicationRoot referenced here
                "width": 800,
                "height": 600
            },

Although Finsemble variables must always be defined directly under the finsemble property, they don't all have to be defined in the manifest file to be directly under finsemble. They can also be defined at the top-level of any import file then referenced elsewhere. This makes Finsemble variables very accessible for application-level configuration as well as third-party component configuration.


Partitioning Finsemble's configuration using import files

As described in the introduction above, Finsemble's configuration starts in the OpenFin manifest file. However, Finsemble was designed to easily support multiple configuration files, providing a much better organization for config settings. Given that JSON doesn't have any kind of "import" or "include" capabilities, Finsemble defines two special import properties into JSON that can be used by the developer to dynamically import other config files. This importing is part of the start-up "assembling" done by the Config Service. The two properties and their functions are as follows:

  • "importConfig" defines an array of JSON URLs to be imported into the top-level finsemble object. Note that this will overwrite any existing config settings, with two exceptions:

    1) New services defined under finsemble.services will be added to the list of existing services (as opposed to replacing the existing list of services).

    2) New components defined under finsemble.components will be added to the list of existing components (as opposed to replacing the list of existing components).

    Regardless of the two exceptions above, a new service or component with the same name as an existing one will replace the existing definition.

  • "importThirdPartyConfig" defines an array of JSON URLs to be imported into the top-level finsemble object. This import is essentially the same as importConfig with one notable difference: the imported configuration settings cannot overwrite any existing settings. In this case, the settings for any potential overwrite will be discarded with a warning message written to the Config Service's log.

Again, the following snippet from the OpenFin manifest file shows the application-level config is being pulled from another file.

    "finsemble": {
        "applicationRoot": "http://localhost:3375/yourSubDirectory/dist",
        "moduleRoot": "http://localhost:3375/yourSubDirectory/node_modules/@chartiq/finsemble/dist",
        "importConfig": [
            "$applicationRoot/configs/application/config.json"
        ]
    }

Below are the seed project's contents for that import file, configs/application/configs.json. Note that four more config files are imported at the bottom.

    {
        "comment": "Top-level application config for development, adding on top of the core config",
        "isAuthEnabled": false,
        "defaultStorage": "localStorage",
        "debugServiceDelay":0,
        "storage": {},
        "betaFeatures": {
            "docking": {
                "enabled": true
            },
            "assimilation": {
                "enabled": false,
                "blacklist": [],
                "whitelist": [],
                "onlySpawned": false
            }
        },
        "importConfig": [
            "$applicationRoot/configs/application/components.json",
            "$applicationRoot/configs/application/systemComponents.json",
            "$applicationRoot/configs/application/services.json",
            "$applicationRoot/configs/other/defaultWorkspaces.json"
        ]
    }

The Central Logger is a unified console for viewing messages across all components and services. It can be used to view import errors and debug the configuration process.


Finsemble configuration contents

The Config Service assembles all of Finsemble's configuration as properties of a single finsemble config object. The configuration settings contained in the Finsemble object are listed below.

  • finsemble.services: contains config settings for all Finsemble services (indexed by service name)
  • finsemble.components: contains config settings for all Finsemble components (indexed by component name)
  • finsemble.defaultStorage: identifies the default data store used by the Storage Client
  • finsemble.isAuthEnabled: enables log-in authentication
  • finsemble.betaFeatures: contains configuration for Finsemble beta features, specifically if they are enabled or disabled; by default, beta features are always disabled (i.e., false)
  • finsemble.workspaces: contains custom settings for workspace configuration

Using the Config Client to retrieve Finsemble's configuration

The Config Client can return a copy of all or part of the finsemble object. Below are some examples using the Config Client to retrieve configuration data. The Config Client is functionally similar to the Distributed Store Client.

Getting all of the Finsemble configuration:

FSBL.Clients.ConfigClient.getValue({field: "finsemble"}, function(err, finsembleConfig) {
    console.log(finsembleConfig);
});

Getting a list of all the configured components:

FSBL.Clients.ConfigClient.getValue({field: "finsemble.components"}, function(err, components) {
    console.log(components);
});

Checking config to determine whether a specific beta feature is enabled:

FSBL.Clients.ConfigClient.getValue({field: "finsemble.betaFeatures.docking.enabled"}, function(err, dockingEnabled) {
    console.log(dockingEnabled);
});

Router configuration

The router's configuration should be in the OpenFin manifest file under the Finsemble property, as shown below. This configuration specifies which router transport to use for same-domain windows and which transport for cross-domain windows. However, if the configuration is not included in the manifest, the default values will be the same as below.

    "finsemble": {
        . . .
        "router" : {
            "sameDomainTransport": "SharedWorker",
            "crossDomainTransport": "OpenFinBus"
        },
    }

Typically, the "SharedWorker" transport is use for all same-domain communication because it is the fastest transport. Also, the OpenFin InterApplication Bus (IAB) is used for all cross-domain communication. However, as of OpenFin version 8.56.28.36, there are cases where the IAB has problems with windows including iFrames, so an alternate transport, "FinsembleTransport", can also be used for cross-domain communcation (i.e., "crossDomainTransport": "FinsembleTransport").

If "FinsembleTransport" is used, then the manifest must also include the following under OpenFin's appAssets:

    "appAssets": [
        {
            "src": "https://finsemble.chartiq.com/finsemble//hosted/FinsembleRouter.zip",
            "version": "1.0.0",
            "alias": "FinsembleRouter",
            "target": "FinsembleRouter.exe"
        }
    ]

Note In the "src" value above, the URL to the FinsembleRouter.zip must match the location of the Finsemble module.

More on configuration format and imports

It is important to understand that all configuration properties are always inserted directly under the Finsemble object, independent from where the import occurred. In other words, regardless of where the JSON data is pulled from, it is always defined under finsemble.

For example, consider the following import contents contained in the file someConfig.json:

    {
        "myConfigValue": {
            "first": 1,
            "last": 99
        },
        "myOtherConfigValue": 456,
        "services": {
            "aChatService": {
                    "visible": false,
                    "active": true,
                    "name": "aChatService",
                    "html": "achat/chat.html",
                    "file": "achat/chatService.js"
            }
        },
        "importConfig": [
            "configs/application/myOtherConfig.json"
        ]
    }

This references the import file configs/application/myOtherConfig.json with the following contents:

    {
        "myConfigValue": {
            "first": 0,
            "last": 100
        },
        "myNewConfigValue": 0,
    }

After the first pass of processing the data (all data but the imports) in someConfig.json, the equivalent of the following will exist in the finsemble object:

    finsemble.myConfigValue = {"first": 1, "last": 99};
    finsemble.myOtherConfigValue = 456;
    finsemble.services.aChatService = {"visible": false, "active": true, ...};

Then, after configs/application/myOtherConfig.json is imported (from the importConfig), the finsemble object will effectively contain:

    finsemble.myConfigValue = { "first": 0, "last": 100};
    finsemble.myOtherConfigValue = 456;
    finsemble.myNewConfigValue = 0;
    finsemble.services.aChatService = {"visible": false, "active": true, ...};

Note the value of myConfigValue was overwritten by the import. Also, a new service named aChatService was added to the service list.


User preferences

Configuration is applied in three waves:

  1. Default application config. This comes from your server.
  2. Dynamic configuration. This is optional, but is mainly used to distribute entitlements to different users.
  3. User preferences. This is pulled from storage.

User preferences are developer-defined options that give users the ability to fine-tune their Finsemble experience. They are set prior to the initialization of the Finsemble services, and are available as a config (similar to dynamic configuration). The user can only modify what you give them access to.

Our seed project allows users to customize the following:

  • Import and export workspace templates. This allows users to create and share the configurations that are most effective for their workflows and share them.
  • Rename workspaces.
  • Create new workspaces based on a template.
  • Specify which workspace will load on start-up.

The API methods for preferences are FSBL.Clients.ConfigClient.setPreference and FSBL.Clients.ConfigClient.getPreferences.

User preferences will always overwrite any config that comes before it. This means they are powerful and should be implemented carefully.


FSBLHeader

You can inject one of our presentation components, the Finsemble Window Title Bar, via the config. This will place the Finsemble Window Title Bar over a component's normal title bar. This is done by setting FSBLHeader to true. FSBLHeader is located in: config -> foreign -> components -> Window Manager -> FSBLHeader.

The FSBLHeader has a variety of parameters, as per the Window Client:

  • component: This specifies the component to inject. The default is the Finsemble Windows Title bar.
  • bumpElements: This changes the position of the FSBLHeader. It can either be:
    • fixed: Either false, "all", or "0Positioned". If "all", all fixed elements are moved. "0Positioned" only moves elements that have top 0. The default is "all".
    • absolute: Either false, "all", or "0Positioned". If "all", all fixed elements are moved. "0Positioned" only moves elements that have top 0. The default is "all".
    • bumpBy: This specifies the amount of pixels to move the FSBLHeader. The default is 32 px.
  • bodyMarginTop: This sets the body margin. The default is 30 px.
  • forceHeaderHeight: This sets the height to the main FSBLHeader div.

Additional notes

  1. importConfig and importThirdPartyConfig are always processed last in a file, independent from where they are placed in the JSON. For this reason it's a good practice to put imports at the bottom.
  2. Each URL in a list of imports will be completely assembled before going to the next URL in the list. Given that any imported file can contain other imports, the process of importing operates recursively in a depth-first fashion. This provides a well-defined ordering when imports redefine config settings.
  3. Any property directly defined under finsemble that matches the RegEx /comment.*/ will be stripped out at run-time (e.g., finsemble.comment, finsemble.comment1).
  4. Configuration processing, as well as error reporting, can be seen in the Config Service's console output.

Further reading

Read the API documentation about the Config Client for additional information.

You can also look at the Config Reference which explains the configuration tree.

See Debugging Finsemble for general instructions on error reporting.

For a discussion about dynamic configuration, as opposed to the static configuration described here, check out the Dynamic Configuration tutorial.