diffusion
Version:
Diffusion JavaScript client
670 lines (644 loc) • 29.5 kB
JavaScript
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
*/