Overview
The FIM JavaScript library facilitates the development of JavaScript client applications that operate with Functional items. It works only in the web browser, because it uses browser specific APIs.
In order to access the JavaScript Library, use the following URL: http://<host>:<port>/fim/fim.js, where <host>:<port> are the ones used by the targeted Bosch IoT Gateway Software runtime.
This will allow you to retrieve Functional Items, invoke their operations, reading and changing their properties values, and also to receive events on any property change.
The FIM JavaScript Library is also available as a npm package, which can be found in runtime/osgi/lib/web/fim/npm directory.
FIM JavaScript Library Basic Guidelines
Event Mechanism
A loosely coupled subscribe/publish eventing mechanism is based respectively on the Subscribable and Publisher interfaces.
An event subscription is made by providing a channel and a callback, which will be invoked when an event is received for the given channel. The interface has two methods for creating subscriptions:
A message is published by providing a channel and optionally a message. The interface has two methods:
See in JavaScript API docs: Subscribable, Publisher, Disposable.
Functional Item Manager
The FunctionalItemManager is responsible for storing and managing a collection of IFunctionalItem instances. It can be created without any arguments, but the activateAsync method must be invoked always before using the manager. It activates the manager by loading the aggregated metadata (on first call only), activates the data service and subscribes for updates. activateAsync can be invoked multiple times.
Example:
var manager = new FunctionalItemManager();
manager.activateAsync().then(() => {
//Functional item manager is activated
});
Getting Items
The methods, that the manager exposes, allow getting IFunctionalItem or a collection of IFunctionalItem instances by UID or by LDAP filter. The FunctionalItemManager methods allow getting items:
Note that the methods, which return Functional items from the cache, will not make remote API calls, if the item is missing. In this case, null will be returned. All other get methods, that have the 'Async' suffix, make remote calls and depending on the passed options, the cache is checked before the remote call. The 'async' methods return a promise that fulfils the requested IFunctionalItem.
See in JavaScript API docs: FunctionalItemRequestOptions.
Example:
manager.getFunctionalItemById('fim:group:admin'); //null, because the cache is still empty
manager.getFunctionalItemByIdAsync('fim:group:admin').then((item) => {
console.log(item); //FunctionalItem {uid: "fim:group:admin", ...}
});
Functional Items Generation
The FunctionalItemManager generates functional item objects, that implement the IFunctionalItem interface. The generated items are cached.
Functional Items Factory
FunctionalItemsFactory allows generation of functional items, registration of constructor functions that return an IFunctionalItem, and registration of initialization functions.
The create method accepts a FunctionalItemDTO and metadata, and returns an IFunctionalItem. It retrieves the object class from the FunctionalItemDTO and finds the appropriate constructor and initialization functions for generating the item. In order to ensure that a constructor/initialization function for the object class is available, it loops through the FunctionalItemDTO's objectClass property and checks for every object class if a registered constructor/initialization function exists. If none is found, it returns the default ones.
See in JavaScript API docs: FunctionalItemsFactory, FunctionalItemConstructor, FunctionalItemDTO.
Events Subscription
The FunctionalItemManager is Subscribable - it allows event subscription for the following events, that the manager processes:
The subscribe and subscribeOnce methods do not make remote subscriptions - they make local subscriptions for the events that the manager processes. The following aliases are available:
The event handlers are invoked with event object, which has functions for retrieving different data from the event (the changed property name, the executed operation, getting the items UID, object class, factory UID and others). For different events, there are different get methods, that are applicable - for example if you receive "operation-executed" event and you invoke getPropertyName, it will return undefined.
Example:
//manager is existing FunctinalItemManager instance
var subscription = manager.subscribe(("functional-item-property-changed", (eo) => {
//process the event object
eo.getPropertyName(); //the name of the changed property
});
//dispose the subscription
subscription.dispose();
See in JavaScript API docs: FunctionalItemManager, FunctionalItemEventObject, FunctionalItemManagerRemoteEventAlias, FunctionalItemManagerUpdateParameters.
Dispose Method
The FunctionalItemManager has a dispose() method, that releases the resources used by the manager (DataService instance, cache, MetadataManager, pending event subscriptions). You should call this method, when you are done with the manager - once the method is called, the instance becomes unusable.
Example:
var manager = new FunctionalItemManager();
manager.dispose();
JavaScript Functional Items
In the context of the FIM JavaScript Library, a valid functional item is every object that implements the IFunctionalItem interface. The IFunctionalItem has the following properties:
Also, the base FunctionalItem class exposes other properties, events, static properties and helper methods.
See in JavaScript API docs: IFunctionalItem, FunctionalItem.
Get and Set Property
Assume that in the examples the variable FI is a generated IFunctionalItem.
Functional item properties are accessible - a property can be retrieved and depending on its write access, it can be changed.
Example:
fi.getPropertyAsync('name').then((value) => { //makes remote call
//can process the value
console.log(value); //"Group Admin"
});
fi.setPropertyAsync('name', 'myNewName'); //sets the value remotely
Invoking Operations
Functional item operations can be executed with an invokeOperationAsync method. The parameters are the operation name and the operation arguments (if any). The method makes a call to the API and returns a Promise containing the result.
Example:
fi.invokeOperationAsync('remove', ['fim:group:1']).then((result) => {
//do something after the operation is executed
});
Event Subscription
A Functional item is Subscribable and Publisher. The following channels are available:
Example:
fi.subscribeOnce("property-changed", (eo) => {
//after event is received, the event subscription is disposed
});
var propertyChangedSubscription = fi.subscribe("property-changed", (eo) => {
console.log(eo.newValue); //new property value
console.log(eo.oldValue); //old property value
console.log(eo.getPropertyName()) //property name
});
propertyChangedSubscription.dispose(); //"property-changed" events will be no more handled
fi.subscribe("operation-executed", (eo) => {
console.log(eo.getOperationName()); //the name of the executed operation
});
See in JavaScript API docs: Disposable, FunctionalItemEventObject.
Metadata
The metadata is used by the application to obtain information about functional items, properties, operations and value types. The different metadata objects are cached by the MetadataManager and while the FunctionalItemManager receives events for updating its cache, the MetadataManager does not receive remote events. The metadata changes are identified by the version property that exists on the metadata object. If the remote call for getting metadata is forced (the cache will not be checked), the version property is used for comparison between the new metadata and the cached one. If there is a difference, the new metadata is used.
The different types of metadata are:
See in JavaScript API docs: FunctionalItemMetadata, FunctionalItemPropertyMetadata, FunctionalItemOperationMetadata, FieldMetadata, BeanMetadata, BeanCreationMetadata, TypeMetadata.
FIM JavaScript Library Intermediate Guidelines
Data Service
A DataService is responsible for all remote communications.
Status Property
The DataService has a status property, which indicates what components (flags) of the data service are turned on at the moment. The property is a mask that is formed from the following flags:
See the following example.
Activation (activateAsync)
If the status property does not satisfy your needs, before using a DataService instance, its activateAsync method should be called. The method activates the data service with the provided settings, if such are not provided – it is activated with the currently applied. The available settings for activation are:
The manager has activeStatus property – it is a mask, which is formed according to the provided activation settings. The status and activeStatus properties can be compared after the activation process and if they are different - the DataService activation fails.
Example:
var ds = new DataService();
var dataServiceActivationSettings = {
checkRemoteService: true,
realTimeUpdates: true,
trackRemoteServices: true
};
ds.activateAsync(dataServiceActivationSettings).then(() => {
//DataService activation succeeded and ds.status === ds.activeStatus
console.log(ds.status); //"7"
}).catch((reason) => {
//DataService activation failed and ds.status !== ds.activeStatus
//Let's say, that the check for remote service returns information, that the service is not available. In this case:
console.log(ds.status); //"1"
console.log(ds.activeStatus); //"7"
});
Deactivation (deactivateAsync)
The DataService has a deactivateAsync method that deactivates the service, which is the same as you pass all the settings set to false.
Event Subscription
The DataSerivce is Subscribable and RemoteSubscribable and it exposes the remote event topics for functional item registered/unregistered/property changed/operation executed events.
See in JavaScript API docs: IDataService, DataServiceActivationSettings, DataServiceStatus, RemoteSubscribable, RemoteEventSubscriptionParameters.
Functional Item
Get, Set and Create IFunctionalItem Property
The properties of the functional item representation can be accessed without doing remote API calls. These methods work only with the item's representation and their implementation can be overridden by the provided ModelLibrary.
Example:
fi.createProperty('myProp', 'foo');
fi.getProperty('myProp'); //"foo"
fi.setProperty('myProp', 'bar');
fi.properties['myProp']; //"bar"
Helper Functions
The static methods work with the provided FunctionalItem, IFunctionalItem or FunctionalItemDTO object. Most of them make remote calls without doing client side validation, while the instance methods validate the input parameters on the basis of the provided metadata e.g. the metadata is not checked for the property existence. At any time you can:
Static Properties
The static property FunctionalItem.constants helps in constructing LDAP filters or parsing the raw JSON response. It also has a few more static properties that will not be necessary in common use cases.
Functional Item Manager
You can modify a manager instance after creating it. Here is how:
Example:
var ds = new CustomDataService();
var metadataManager = new CustomMetadataManager();
var manager = new FunctionalItemManager({
dataService: ds,
metadataManager: metadataManager
});
These handlers are called, if a remote subscription is made and the corresponding event is received. You can override these handlers and provide your own implementation at any time.
Example:
var manager = new FunctionalItemManager();
manager.functionalItemPropertyChangedEventHandler = () => {}; //when a "functional-item-property-changed" event is received - nothing will be done.
manager.activateAsync().then(() => {
//manager is activated
});
See in JavaScript API docs: FunctionalItemManagerConfiguration, FunctionalItemManagerUpdateParameters.
Example:
var interruptionsCount = 0;
manager.handleRemoteEventsSubscriptionInterruption = (reason) => {
interruptionsCount++;
if (interruptionsCount === 3) {
console.log('Subscription process failed multiple times.');
}
//restore subscriptions. See next paragraph.
manager.subscribeForUpdatesAsync().then(() => {
//subscription/s done
});
};
Example:
var manager = new FunctionalItemManager({
realTimeUpdates: {
off: true
}
});
manager.activateAsync().then(() => {
//manager is activated, without remote subscription
});
Example:
var manager = new FunctionalItemManager({loadPregeneratedMetadata: false });
There are a few more options that can be found in the documentation.
See in JavaScript API docs: FunctionalItemManagerConfiguration.
Custom Ajax Adapter
You can provide your own implementation of the Ajax Adapter, which can be used by the DataService or any other FIM 'primitive' to make remote HTTP calls.
Implementation
The custom Ajax Adapter must implement the standard Ajax FIM 'interface':
Example:
class SimpleAjaxAdapter {
constructor() {
this.name = 'simple-ajax-adapter'; //id of the adapter
}
initialize() {
//all initializing logic should be here
}
ajax(config) {
//construct and send ajax request
}
}
Registration
After the implementation, the Ajax Adapter needs to be registered by calling the registerAdapter method on the config object (used for registering, initializing and retrieving adapter implementations). The name of the standard FIM 'interface' ("ajax" in this case) and a constructor function that returns an instance of the specified 'interface' need to be provided to the registerAdapter method. Multiple implementations can be registered under a specific standard FIM 'interface'.
Example:
config.registerAdapter('ajax', SimpleAjaxAdapter);
Initialization
The custom Ajax adapter needs to be initialized before the activation of the FunctionalItemManager or any other FIM 'primitive' that makes HTTP requests. The adapter is initialized after the config.initialezeAdapterInstance method is invoked. The name of the standard FIM 'interface' ("ajax" in this case) and the custom adapter's name/ID need to be provided, as well as a boolean value that indicates whether this is a default (standard FIM 'interface') adapter, or not. This boolean value is true by default and overrides the default instance. TheinitialezeAdapterInstance method returns an initialized instance of the specific adapter, which means that config.getAdapterInstance does not need to be called.
Example:
var ajaxAdapter = config.initializeAdapterInstance('ajax', 'simple-ajax-adapter', true);
// a "requestInterceptor" can be add if needed
ajaxAdapter.requestInterceptor = (requestInfo) => {
// modify/check the request information before the http call
};
See in JavaScript API docs: Ajax, AjaxRequestInfo, AjaxConfig, Config.
Additional Web Frameworks Data Binding Support
The library offers extension points that allow modification of the FunctionalItem so that the Data Binding Model of the framework of use fits better. Two adapters are provided out-of-the-box:
Some of the supported frameworks are:
Setting up Additional Web Frameworks
By Implementing a Custom ManagedFunctionalItem
The first approach is to implement a custom ManagedFunctionalItem and register it in the FunctionalItemManager. With this procedure the focus should be on the getProperty, setPropety and createProperty. For more information please check the JavaScript API documentation.
One way to implement a custom ManagedFunctionalItem is to extend the FunctionalItem class and override the getProperty, setPropety and createProperty.
By implementing a ModelLibrary
A Model Library Adapter instance needs to be initialized via an identifier for registration - modelLibrary. The ModelLibrary interface is using the singleton pattern and works with the following hooks:
Knockout is provided out-of-the-box by the ModelLIbrary adapter and works with the initializeConstructor hook.
Angular users can switch to Observables by using one of the two above-mentioned approaches.
See in JavaScript API docs: FunctionalItemManager, ModelLibrary, ManagedFunctionalItem.
API reference
For more information and detailed API reference please visit the JavaScript API documentation.