UNPKG

diffusion

Version:

Diffusion JavaScript client

670 lines (644 loc) 29.5 kB
var _interface = require('util/interface')._interface; /** * Topic control feature. * <P> * <br /> * Provides methods to change and update the topic tree stored on the server. * @example * // Get a reference to topic control feature * var topics = session.topics; * * @namespace Session.topics */ module.exports.TopicControl = _interface('TopicControl', [ /** * Add a topic to the server at a specific path. This returns a {@link Result}. * <P> * The path should be a string. To express hierarchies, <code>/</code> can * be used as a delimiter. This allows topics to be nested and grouped below * each other. For example, <code>session.topics.add('foo/bar');</code> * creates the topic <code>bar</code>. A topic is not created at <code>foo</code> * by this method. * <P> * Each topic has a particular {@link diffusion.topics.TopicType type}, which constrains the kind of values that the * topic will allow. This type can either be explicitly provided, or included as part of a * {@link diffusion.topics.TopicSpecification TopicSpecification}. * <P> * <h5>Adding from topic type</h5> * <P> * To directly specify the type of topic to create, provide a string path and a {@link diffusion.topics.TopicType}. * Topics specified in this way are created with default topic properties, as described in * {@link diffusion.topics.TopicSpecification}. * <P> * <h5>Adding from topic specification</h5> * <P> * {@link diffusion.topics.TopicSpecification TopicSpecifications} allows the creation of topics of a particular * type, along with additional properties that determine how the topic operates. For instance, you may wish to * specify that a topic will validate values before publishing, or that it will only publish values instead of * deltas. * <P> * <h5>Operation results</h5> * <P> * If the topic was added, or a topic already exists with the same path and specification, the operation will * succeed. If there is a problem with adding the topic then the result will be rejected with an error. * <P> * If any sessions have already subscribed to the same path that a topic is created for, they will receive a * <code>subscription</code> event once the topic is added, and a <code>value</code> event with the initial value * (if supplied). * <P> * If the session is closed when calling this method, the returned result will be rejected. * <P> * <h5>Failure</h5> * <P> * If the operation fails a {@link diffusion.topics.TopicAddFailReason} is provided. Adding a topic may fail because * the session has insufficient permissions; a topic already exists at the specified path; or certain mandatory * {@link diffusion.topics.TopicSpecification TopicSpecification} properties were missing * * * @example * // Create a topic with a Topic Type * session.topics.add('foo/binary', diffusion.topics.TopicType.BINARY); * * @example * // Create a topic with a TopicSpecification * const TopicSpecification = diffusion.topics.TopicSpecification; * var specification = new TopicSpecification(diffusion.topics.TopicType.JSON, { * TopicSpecification.VALIDATE_VALUES : "true" * }); * * session.topics.add('foo/json', specification); * * @example * // Handle the add topic result * session.topics.add('foo/bob', diffusion.topics.TopicType.JSON).then(function(result) { * if (result.added) { * console.log('Topic added'); * } else { * console.log('A compatible topic already exists'); * } * }, function(error) { * console.log('Topic add failed: ', error); * }); * * @param {String} topicPath - The topic path to create. * @param {diffusion.topics.TopicType|diffusion.topics.TopicSpecification} [specification] - * The topic type/specification * @returns {Result<Session.topics.TopicAddResult>} A {@link Result<Session.topics.TopicAddResult>} * for this operation * @function Session.topics#add */ 'add', /** * Remove one or more topics at the server. * <P> * The topics to remove will depend upon the nature of the topic selector * specified. If the selector does not have {@link TopicSelector descendant * pattern qualifiers} (i.e. / or //), only those topics that exist at paths * indicated by the selector will be removed and not their descendants. If a * single / qualifier is specified, all descendants of the matching topic * paths will be removed. If // is specified, all branches of the topic tree * that match the selector (i.e topics at the selected paths and all * descendants of the selected paths) will be removed. * <P> * This function can take any number of arguments. Each argument can be a string * or a {@link TopicSelector}. Alternatively, an array of strings and * {@link TopicSelector}s can be passed as a single argument. * * @param {...String | TopicSelector | String[] } * selector - The selector specifying the topics to remove * @returns {Result<undefined>} A {@link Result<undefined>} for this operation * @function Session.topics#remove * * @example * // Remove the topic at 'foo/bar', leaving descendants * session.topics.remove('>foo/bar'); * * @example * // Remove the topic at 'foo/bar' and all descendants * session.topics.remove('?foo/bar//'); */ 'remove', /** * Register a deferred action to remove a branch of the topic tree when this * session is closed. * <P> * A removal action can be registered at any point in the topic tree, but * can not be placed above or below existing registrations. An * <code>error</code> event will be emitted if the server rejects the * registration. * <P> * When this session is closed, regardless of reason, this topic and all * topics below it will be removed from the topic tree. * <P> * Multiple sessions can request that the same branch be removed. If a branch * has multiple registrations, then the marked topics will not be removed * until all registered sessions have been closed. * <P> * When registration is successful, the {@link Result} will * call the success callback with an object representing the registration * with the property function deregister that can be called at any point to * remove this registered action. The deregistration function returns a new * {@link Result}. * <P> * If the session is closed when calling this method, the returned result will emit an <code>error</code> event. * <p> * <strong> Deprecated since 6.1 </strong> * <p> * The preferred method for automatic removal of topics is * the {@link TopicSpecification#REMOVAL REMOVAL} topic * property. To achieve the equivalent of this method the * property can be specified as:- * <p> * <code> * when this session closes remove "?topicPath//"</code> * <p> * To achieve a dependency upon more than one session, a * condition specifying a principal name or some other session * property can be used. * <p> * This method will be removed in a future release. * * @example * // Remove all topics under 'foo' * session.topics.removeWithSession('foo').then( * function(registration) { * // Registration complete * * // Deregister this action * registration.deregister().then( * function() { * // Deregistration complete * }, * function(err) { * // Failure while deregistering * } * ); * }, * function(err) { * // Could not register * } * ); * * @param {String} topicPath - The path of the topic tree to remove * @returns {Result<Session.topics.RemoveWithSessionResult>} Registration * {@link Result<Session.topics.RemoveWithSessionResult>}. * @function Session.topics#removeWithSession */ 'removeWithSession', /** * Update a topic on the server with a new supplied value. The returned {@link Result} will complete if the update * is successfully applied, and any sessions subscribed to the same topic will be notified of the new topic value. * <P> * If the session is closed when calling this method, the returned result will also emit an <code>error</code> * event. * <P> * <h5>Failure</h5> * <P> * If the operation fails a {@link UpdateFailReason} is provided. The value provided must be compatible with the * data type of the topic being updated, or the update will be rejected. Updates will also be rejected if the * session has insufficient permissions, or the topic does not exist. * <P> * Prefer {@link Session.topics#updateValue} when updating a <code>Double</code> or <code>Int64</code> topic. This * method can infer the wrong data type when updating a <code>Double</code> topic with a value that does not * have a fractional component. * * @example * // Update topic 'foo/bar' with string value. * session.topics.update('foo/bar', 'baz'); * * @example * // Update topic with JSON content * var content = diffusion.datatypes.json().from({ "foo" : "bar" }); * * session.topics.update('foo/bar', content); * * @param {String} path - The topic path to update * @param {Object} value - The value to update the topic with * @returns {Result<String>} A {@link Result<String>} for this operation. The value is the topic path that has * been updated. * @function Session.topics#update * * @deprecated since 6.2 * <p> * This method is deprecated. Use {@link Session.topicUpdate#set * topicUpdate.set} instead. This method will be removed in a * future release. */ 'update', /** * This method is similar to {@link Session.topics#update} but takes in a data type so that the updater can * determine which data type to use when encoding the value. * * <P> * Note that if a double value is applied to an <code>Int64</code> topic, the fractional component is ignored. * Updating a topic with a different value type can lead to an unexpected value getting applied to the topic. * * @example * session.topics.updateValue('foo', 123.45, diffusion.datatypes.double()); * * @param {String} path - The topic path to update * @param {Object} value - The value to update the topic with * @param {DataType} datatype - The data type to be used for encoding the value * @returns {Result<String>} A {@link Result<String>} for this operation. The value is the topic path that has * been updated. * @function Session.topics#updateValue * * @deprecated since 6.2 * <p> * This method is deprecated. Use {@link Session.topicUpdate#set * topicUpdate.set} instead. This method will be removed in a * future release. */ 'updateValue', /** * Register a handler to provide exclusive updates for a particular branch of the topic tree. Once successfully * registered, the handler will be called with lifecycle callbacks. This grants this session sole access to publish * updates to topics at or under the branch used for registration. * <P> * If no other handlers have been registered for the topic path, the handler will enter the * {@link TopicUpdateHandler#onActive active} state. This provides an {@link Updater updater} which can then * be used to publish updates for topics at or below the registered topic path. * <P> * If there is an existing handler for the topic path, the handler will be put into the * {@link TopicUpdateHandler#onStandby standby} state. This indicates that the handler is registered but does not * have access to publish updates. Once all previously registered handlers are closed, this handler will transition * to the {@link TopicUpdateHandler#onActive active} state. * <P> * The handler will be closed if the session closes or {@link TopicUpdateHandlerUpdater#close updater#close} is * called. This is a terminal state from which no further state transitions will occur. When a registered handler * is closed, if there is another handler registered by a different session, this next handler will transition to an * active state. * <P> * Handlers cannot be registered above or below the topic path of any other registered handlers. Attempting to do * so will close the handler. * * @example * session.topics.registerUpdateSource('foo/bar', { * onRegister : function(topicPath, unregister) { * // The handler has been registered * * // Unregister the handler * unregister(); * }, * onActive : function(topicPath, updater) { * // Now that we're active, we have sole write access for all topics under 'foo/bar' * updater.update('foo/bar/baz', 123).then(function() { * // Updates return a promise just like session.topics#update * }); * }, * onStandby : function(topicPath) { * // The updater is registered, but another updater currently holds the active state. * }, * onClose : function(topicPath) { * // The updater is closed * } * }); * * @example * // 'client' is an anonymous session that has insufficient permission to register an update source * client.topics.registerUpdateSource('foo/bar', { * onRegister : function(topicPath, unregister) { * }, * onActive : function(topicPath, updater) { * }, * onStandby : function(topicPath) { * }, * onClose : function(topicPath, error) { * // The updater is closed because the error is diffusion.errors.ACCESS_DENIED * } * }); * * @param {String} path - The topic path to register an update source for. * @param {TopicUpdateHandler} updateHandler - handler specifies the handler for the specified * branch (unless overridden by a handler registered against a more specific branch) * @returns {Result<undefined>} a {@link Result<undefined>} for this operation * @function Session.topics#registerUpdateSource * * @deprecated since 6.2 * <p> * This method is deprecated. Use {@link * Session.topicUpdate#createUpdateStream * topicUpdate.createUpdateStream} instead. This method will be * removed in a future release. */ 'registerUpdateSource', /** * Register a {@link MissingTopicHandler} to handle requests for a branch of * the topic tree. * <p> * The provided handler is called when a client subscribes or fetches using * a topic selector that matches no existing topics. This allows a control * client to intercede when another session requests a topic that does not * exist. The control client may {@link Session.topics.add create the topic}, * perform some other action, or do nothing, before allowing the client * operation to proceed by calling {@link MissingTopicNotification#proceed() proceed()}. * Alternatively, the control client can call {@link MissingTopicNotification#cancel() * cancel()} to discard the request. * * <p> * A control client can register multiple handlers, but may only register a * single handler for a given topic path. See * {@link MissingTopicHandler#onRegister}. * A handler will only be called for topic selectors with a * {@link TopicSelector#prefix path prefix} that starts with or is * equal to <code>topicPath</code>. If the path prefix matches multiple handlers, * the one registered for the most specific (longest) topic path will be * called. * <P> * If the session is closed or the handler could not be registered, the returned * {@link Result} will call its failure callback, and the handler's * {@link MissingTopicHandler#onClose} or {@link MissingTopicHandler#onError} method * will be called. * * @param {String} topicPath identifies a branch in the topic tree * * @param {MissingTopicHandler} handler specifies the handler for the specified branch (unless * overridden by a handler registered against a more specific branch) * * @returns {Result<undefined>} A {@link Result<undefined>} for this registration * * @function Session.topics#addMissingTopicHandler */ 'addMissingTopicHandler' ]); /** * Handler called when a client session subscribes or fetches using a topic * selector that matches no topics. This interface must be implemented by the user. * <P> * Handler instances can be registered using * {@link Session.topics.#addMissingTopicHandler addMissingTopicHandler}. * * @class MissingTopicHandler */ module.exports.MissingTopicHandler = _interface('MissingTopicHandler', [ /** * Called when a client session requests a topic that does not exist, * and the topic path belongs to part of the topic tree for which this * handler was registered. * * <p> * Missing topic notifications only occur when using the deprecated * {@link Session#fetch} mechanism. The newer {@link Session#fetchRequest} * mechanism does not generate missing topic notifications. * * <p> * The handler implementation should take the appropriate action (for * example, create the topic), and then call * {@link MissingTopicNotification#proceed() proceed} on the supplied * <code>notification</code>. This allows the client request to continue and * successfully resolve against the topic if it was created. * * <p> * Alternatively, the handler can call * {@link MissingTopicNotification#cancel() cancel} to discard the * request. A handler should always call <code>proceed</code> or * <code>cancel</code>, otherwise resources will continue to be reserved * on the server until the notification times out. * * @param {MissingTopicNotification} notification - The missing topic notification * * @function MissingTopicHandler#onMissingTopic */ 'onMissingTopic', /** * Called when the handler has been successfully registered with the server. * <P> * A session can register a single handler for a given branch of the topic tree. If there is already a handler * registered for the topic path the operation will fail and {@link MissingTopicHandler#onClose onClose} will be * called. * <P> * To deregister the handler, call the <pre>deregister</pre> function supplied. * * @param {String} path - The registration path * @param {Function} deregister - A function that may be called to deregister this handler * * @function MissingTopicHandler#onRegister */ 'onRegister', /** * Called when the handler is closed. The handler will be closed if the session is closed, or if the handler is * unregistered. * <P> * Once closed, no further calls will be made for the handler. * * @param {String} topicPath - The registration path * * @function MissingTopicHandler#onClose */ 'onClose', /** * Notification of a contextual error related to this handler. This is * analogous to an unchecked exception being raised. Situations in which * <code>onError</code> is called include the session being closed before the * handler is registered, a communication timeout, or a problem with the * provided parameters. No further calls will be made to this handler. * * @param {String} topicPath - The registration path * @param {Object} error - The error * * @function MissingTopicHandler#onError */ 'onError' ]); /** * Notification that a session has made a request using a selector that does * not match any topics. * <P> * Processing of the initial request will be halted until * {@link MissingTopicNotification#proceed proceed} is called, at which point * the selector will be resolved against the topic tree again. * <P> * If after calling <code>proceed</code> the selector still does not * match against any topics, no further notifications will be provided. * <P> * Should {@link MissingTopicNotification#cancel cancel} be called, or the * notification time out, the request will be discarded. The requesting * session will not be notified that their request has been cancelled. * * @class MissingTopicNotification * @property {String} path - The common root topic path derived from the requested topic selector * @property {TopicSelector} selector - The topic selector that triggered this notification * @property {String} sessionID - Session ID of the client session that triggered this notification */ module.exports.MissingTopicNotification = _interface('MissingTopicNotification', [ /* * The common root topic path derived from the requested topic selector */ 'path', /* * The topic selector that triggered this notification */ 'selector', /* * Session ID of the client session that triggered this notification */ 'sessionID', /** * Instruct the server to complete processing of the session request. * <P> * This may be called after additional operations (such as adding * topics) have been performed, to allow the requested selector to be * resolved against the updated topic tree. * * <p> * For subscription requests, the topic selector will be added to the * client's topic selections. This will cause the client session to * become subscribed to topics that match the selector if they are added * later. * * @function MissingTopicNotification#proceed */ 'proceed', /** * Cancel the client request on the server. * <P> * Calling this will prevent any further processing of the request. For * subscription requests, the topic selector will be discarded. The * client session will not become subscribed to topics that match the * selector if they are added later. * * @function MissingTopicNotification#cancel */ 'cancel' ]); /** * The TopicUpdateHandler interface for exclusive updates. This interface must be implemented by the user, to be * registered via {@link Session.topics#registerUpdateSource}. * <P> * <br /> * A topic update handler has a lifecycle that reflects the registration state on the server. This is expressed * through the callback methods. Once {@link Session.topics.TopicUpdateHandler#onClose onClose} has been called, no * further interactions will occur. * <P> * When an update handler is registered it will be notified via the {@link TopicUpdateHandler#onRegister onRegister} * callback. Once registered it may be in either a <pre>active</pre> state, where it can provide topic updates, or a * <pre>standby</pre> state, where it is still registered but is not allowed to perform updates. The state may be * switched in any order, depending on server policy. * * @class TopicUpdateHandler * * @deprecated since 6.2 * <p> * This class is deprecated. It is only used in conjunction with * {@link Session.topics#registerUpdateSource}. Use {@link * Session.topicUpdate#createUpdateStream * topicUpdate.createUpdateStream} instead. This method will be * removed in a future release. */ module.exports.TopicUpdateHandler = _interface('TopicUpdateHandler', [ /** * Called when the handler has been successfully registered with the server. * <P> * A session can register a single handler for a given branch of the topic tree. If there is already a handler * registered for the topic path the operation will fail and {@link TopicUpdateHandler#onClose onClose} will be * called. * <P> * To deregister the handler, call the <pre>deregister</pre> function supplied. * * @param {String} topicPath - The path that the handler is registered for * @param {Function} deregister - A function that may be called to deregister this handler * @function TopicUpdateHandler#onRegister */ 'onRegister', /** * State notification that this handler is now active for the specified topic path and is therefore in a valid * state to send updates on topic at or below the registered topic path * * @param {String} topicPath - The registration path * @param {Updater} updater - An updater that can be used to update topics * @function TopicUpdateHandler#onActive */ 'onActive', /** * State notification that this handler is not currently allowed to provide topic updates for the specified topic * path. This indicates that another {@link TopicUpdateHandler} is currently active for the given topic path. * <P> * Server policy will dictate when this handler is set as active. * <P> * If this handler was previously in a <pre>active</pre> state, any {@link Updater} instances for this topic path * will no longer be valid for use. * * @param {String} topicPath - The registration path * @function TopicUpdateHandler#onStandBy */ 'onStandBy', /** * Called when the handler is closed. The handler will be closed if the session is closed, or if the handler is * unregistered. * <P> * Once closed, no further calls will be made for the handler. * * @param {String} topicPath - The registration path * @param {diffusion.errors} errorReason - An optional value representing the error; this can be one of the * constants defined in {@link diffusion.errors}, or a feature-specific reason. It is absent if the handler * was closed because the session closed. * @function TopicUpdateHandler#onClose */ 'onClose' ]); /** * An updater provides methods to update a topic on the server with a new supplied value, like {@link * Session.topics#update}, but within the context of an exclusive {@link TopicUpdateHandler} registration. * If the update is successful it will call the result's success callback, and any sessions subscribed to the same * topic will be notified of a topic update. * <P> * An updater may only update topics at or below the registration path of the {@link TopicUpdateHandler} from which it * was produced. * <P> * The result will fail if the update was not successful. It is necessary for the topic to * exist, and that the value type must be valid for the topic, for example a topic added with {@link Metadata.Integer} * cannot accept a string value. Updates will also fail if the {@link TopicUpdateHandler} this updater was created from * is in a <pre>standby</pre> or <pre>closed</pre> state. * * @example * updater.update('foo/bar', 123).then(function() { * // Update successful * }, function(err) { * // Update failed * }); * * @class Updater * * @deprecated since 6.2 * <p> * This class is deprecated. It is only used in conjunction with * {@link Session.topics#registerUpdateSource}. Use {@link * Session.topicUpdate#createUpdateStream * topicUpdate.createUpdateStream} instead. This method will be * removed in a future release. */ module.exports.Updater = _interface('Updater', [ /** * Update a topic * * <P> * Prefer {@link #updateValue} when updating a <code>Double</code> or <code>Int64</code> topic. This * method can infer the wrong data type when updating a <code>Double</code> topic with a value that does not * have a fractional component. * * @param {String} topicPath - The topic to update * @param {Object} value - The value to update the topic with * @return {Result<undefined>} - The {@link Result<undefined>} of the update call * @function Updater#update */ 'update', /** * Update a topic with a specified data type. * * @param {String} topicPath - The topic to update * @param {Object} value - The value to update the topic with * @param {Datatype} datatype - The data type to be used for encoding the value * @return {Result<undefined>} - The {@link Result<undefined>} of the update call * @function Updater#updateValue */ 'updateValue' ]); /** * @typedef {Object} Session.topics.TopicAddResult * @property {Boolean} added - whether the Topic was added or not * @property {String} topic - the Topic path that was used */ /** * @typedef {Object} Session.topics.RemoveWithSessionResult * @property {Function} deregister - function to remove this registered action */