Skip to content

Latest commit

 

History

History
700 lines (496 loc) · 19.5 KB

peer.markdown

File metadata and controls

700 lines (496 loc) · 19.5 KB

jet.Peer API

Load the module "node-jet":

var jet = require('node-jet');

jet.Peer

The Jet Peer is able to to consume content provided by (other) peers:

The Jet Peer is also able to create content:

jet.Peer([config]) -> peer

Creates and returns a new Jet Peer instance with the specified connection config. The supported config fields are:

  • url: {String} The Jet Daemon Websocket URL, e.g. ws://localhost:11123
  • ip: {String} The Jet Daemon TCP trivial protocol ip (default: localhost)
  • port: {String} The Jet Daemon TCP trivial protocol port (default: 11122)
  • user: {String, Optional} The user name used for authentication
  • password: {String, Optional} The password used for authentication
  • rejectUnauthorized: {Boolean, Optional} Allow self signed server certificates, when using wss:// (default: false)
  • headers: {Object, Optional} Pass additional headers (cookies, bearer etc) to the WebSocket upgrade request

The peer uses either the Websocket protocol or the TCP trivial protocol (default) as transport. When specifying the url field, the peer uses the Websocket protocol as transport. If no config is provided, the Peer connects to the local ('localhost') Daemon using the trivial protocol. Browsers do only support the Websocket transport and must be provided with a config with url field.

Authentication is optional and is explained separately.

var jet = require('node-jet');

var peer = new jet.Peer({
  url: 'ws://jet.nodejitsu.com:80'
});

peer.connect().then(function() {
  console.log('connection to Daemon established');
  console.log('Daemon Info: ', peer.daemonInfo);
});

peer.connect() -> Promise

Connects to the Daemon and returns a Promise which gets resolved as the connection is established. After the connect Promise has been resolved, the peer provides peer.daemonInfo.

peer.connect().then(function() {
   var daemonInfo = peer.daemonInfo;
   console.log('name', daemonInfo.name); // string
   console.log('version', daemonInfo.version); // string
   console.log('protocolVersion', daemonInfo.protocolVersion); // number
   console.log('can process JSON-RPC batches', daemonInfo.features.batches); // boolean
   console.log('supports authentication', daemonInfo.features.authentication); // boolean
   console.log('fetch-mode', daemonInfo.features.fetch); // string: 'full' or 'simple'
});

The returned Promise may be rejected with:

peer.close()

Closes the connection to the Daemon. Returns a promise which resolves as the connection has been closed.

peer.isClosed() -> Promise

Returns a promise which gets resolved when the connection to the Daemon has been closed.

peer.set(path, value, [options]) -> Promise

Tries to set the Jet State specified by path to value. Returns a Promise which gets resolved as the specified state has been setted successfully to the specified value.

peer.set('foo', 123)
	.then(function() {
    	console.log('set finished successfully');
  	}).catch(function(err) {
    	console.log('set failed', err);
	});

// dont care about the result
peer.set('foo', 12341);

options is an optional argument, which supports the following fields:

  • timeout in seconds. Time to wait before rejecting with a timeout error
  • valueAsResult boolean flag. If set true the returned Promise get the "real" new value as argument.
peer.set('magic', 123, {
  timeout: 7,
  valueAsResult: true
}).then(function(realNewValue) {
    console.log('magic is now', realNewValue);
}).catch(function(err) {
    console.log('set failed', err);
});

The returned Promise may be rejected with:

peer.call(path, args, [options]) -> Promise

Calls the Jet Method specified by path with args as arguments. Returns a Promise which may get resolved with the Method call's result. An options object may be specified which may define a

  • timeout in seconds. Time to wait before rejecting with a timeout error
peer.call('sum', [1,2,3,4,5]).then(function(result) {
    console.log('sum is', result);
}).catch(function(err) {
    console.log('could not calc the sum', e);
});


// dont care about the result
peer.call('greet', {first: 'John', last: 'Mitchell'});

The returned Promise may be rejected with:

peer.add(state|method) -> Promise

Registers a jet.State or a jet.Method instance at the Daemon. The returned Promise gets resolved when the Daemon has accepted the request and set (States) or call (Methods) events may be emitted.

var jim = new jet.State('persons/a4362d', {name: 'jim'});

peer.add(jim).then(function() {
  console.log('jim has been added');
});

The returned Promise may be rejected with:

peer.remove(state|method) -> Promise

Unregisters a jet.State or a jet.Method instance at the Daemon. As soon as the returned Promise gets resolved, no set or call events for the state/method are emitted anymore.

var jim = new jet.State('persons/a4362d', {name: 'jim'});

peer.add(jim).then(function() {
  console.log('jim has been added');
  peer.remove(jim).then(function() {
	console.log('jim has been removed');
  });
});

The returned Promise may be rejected with:

peer.fetch(fetcher) -> Promise

Registers the fetcher at the Daemon. A jet.Fetcher must be created first. The returned Promise gets resolved when the Daemon has accepted the fetcher and data event may be emitted.

var topPlayers = new jet.Fetcher()
  .path('startsWith', 'players/')
  .sortByKey('score', 'number')
  .range(1, 10)
  .on('data', function(playersArray) {
  });

peer.fetch(topPlayers);

The returned Promise may be rejected with:

peer.unfetch(fetcher) -> Promise

Unregisters the fetcher at the Daemon. As soon as the returned Promise gets resolved, no data events for the fetcher are emitted anymore.

jet.State

new jet.State(path, value, [access])

  • path: {String} The unique path of the State
  • value: {Any} The initial value of the State
  • access: {Object, Optional} Containing fetchGroups and setGroups

Creates a new State (but does NOT register it at the Daemon). To register the State call peer.add!

var john = new jet.State('peoples/25261', {age: 43, name: 'john'}, {
    fetchGroups: ['public'],
	setGroups: ['admin']
});

peer.add(john).then(function() {
});

state.on('set', cb)

Registers a set event handler. Should be called before the state is actually added (peer.add(state)). The cb callback gets the new requested value passed in.

The function is free to:

  • return nothing, a State change is posted automatically with the newValue
  • throw an Error, the Error should be a String or an Object with code and message
  • return on Object with the supported fields:
    • value: {Any, Optional} the "real/adjusted" new value. This is posted as the new value.
    • dontNotify: {Boolean, Optional} Don't auto-send a change Notification
john.on('set', function(newValue) {
	var prev = this.value();
    if (newValue.age < prev.age){
      throw 'invalid age';
    }
    return {
		value: {
		  age: newValue.age,
		  name: newValue.name || prev.name
		}
	};
});

To provide an async set event handler, provide two arguments to the callback.

  • The requested newValue
  • reply: {Function} Method for sending the result/error.
john.on('set', function(newValue, reply) {
	setTimeout(function() {
		var prev = this.value();
    	if (newValue.age < prev.age){
      		reply({
				error: 'invalid age'
			});
    	} else {
    		reply({
				value: {
		  			age: newValue.age,
		  			name: newValue.name || prev.name
			}});
		}
	}, 200);
});

The arguments to reply can be:

  • value: {Any} The new value of the state.
  • dontNotify: {Boolean} Dont auto notify a state change.
  • error: {String/Error, Optional} Operation failed

state.value([newValue])

If newValue is undefined, returns the current value. Else posts a value change Notification that the State's value is now newValue. Use this for spontaneouos changes of a State which were not initially triggered by the set event handler invokation.

var ticker = new jet.State('ticker', 1);

peer.add(ticker).then(function() {
  setTimeout(function() {
    var old = ticker.value();
    ticker.value(++old);
  },1000);
});

state.add() -> Promise

Register the state from the Daemon convenience function. peer.add(state) must have been called before to initially bind the state to the respective peer!

The returned Promise may be rejected with:

state.remove() -> Promise

Unregister the state from the Daemon. Is the same as calling peer.remove(state).

The returned Promise may be rejected with:

jet.Fetcher

new jet.Fetcher()

Creates a new fetcher. All fetcher calls are chainable. Register the fetcher instance with a call to peer.fetch(fetcher).

fetcher.on('data', fetchCb) -> Fetcher

Installs a callback handler for the data event. The fetchCb argument for non-sorting fetches is an Object with:

  • path: {String} The path of the State / Method which triggered the Fetch Notification
  • event: {String} The event which triggered the Fetch Notification ('add', 'remove', 'change')
  • value: {Any | undefined} The current value of the State or undefined for Methods
var movies = new Fetcher()
  .path('startsWith', 'movies')
  .on('data', function(data) {
    console.log(data.path, data.event, data.value);
  });

peer.fetch(movies);

For sorting fetch rules, the fetchCb arguments are:

  • sortedStatesArray: {Array} The sorted states/methods
var topTenPlayers = new jet.Fetcher()
  .path('startsWith', 'players/')
  .sortByKey('score', 'number')
  .range(1, 10)
  .on('data', function(topTenPlayersArray) {
  });

peer.fetch(topTenPlayers);

fetcher.path(predicate, comp) -> Fetcher

Adds a path matching rule to the fetcher.

Implemented path predicates are:

  • startsWith {String}
  • startsNotWith {String}
  • endsWith {String}
  • endsNotWith {String}
  • contains {String}
  • containsNot {String}
  • containsOneOf {Array of Strings}
  • containsAllOf {Array of Strings}
  • containsOneOf {Array of Strings}
  • equals {String}
  • equalsOneOf {Array of Strings}
  • equalsNotOneOf {Array of Strings}

fetcher.pathCaseInsensitive() -> Fetcher

Makes path matching case insensitive.

fetcher.expression(expr) -> Fetcher

Directly sets the fetch expression rule object. This call overwrite all previously set (chained) rules.

// The expression may contain the following entries
var expr = {
  path: { // path based matches
    caseInsensitive: true,
    startsWith: 'foo',
    contains: ['bar', 'test']
    // and all other path based match rules
  },
  value: { // value based matches
    equals: 3
    // and other value based match rules
  },
  valueField: {
    'sub.object.path': {
      isType: 'string'
      // and other value/key based match rules
    },
    'another.sub.object.path': {
      lessThan: 5
      // and other value/key based match rules
    }
  },
  sort: {
    asArray: true, // pass items as sorted array to data callback. See fetcher.differential doc
    byPath: true, // either this
    byValue: 'number', // or this (needs supposed js type)
    byValueFiels: { // or this
      'sub.object.path': 'string'
    },
    descending: true,
    from: 1, // starts with index 1 (inclusive range)
    to: 100 // last index 100 (inclusive range)
  }
}

fetcher.value(predicate, comp) -> Fetcher

Adds a value matching rule for primitive type values to the fetcher.

Implemented predicates are:

  • lessThan {any less than comparable}
  • greaterThan {any greater than comparable}
  • equals {any primitive type}
  • equalsNot {any primitive type}
  • isType {String}

fetcher.key(keyString, predicate, comp) -> Fetcher

Adds a key matching rule for Object type values to the fetcher. Nested keys can be specified like this: relatives.father.age.

Implemented predicates are:

  • lessThan {any less than comparable}
  • greaterThan {any greater than comparable}
  • equals {any primitive type}
  • equalsNot {any primitive type}
  • isType {String}

fetcher.sortByPath() -> Fetcher

Adds a sort by path rule to the fetcher.

fetcher.sortByValue(type) -> Fetcher

Adds a sort by value for primitive types to the fetcher. Type can be either:

  • number
  • string

fetcher.sortByKey(keyString, type) -> Fetcher

Adds a sort by key for Object types to the fetcher. Type can be either:

  • number
  • string

Nested keys can be specified like this: relatives.father.age.

fetcher.range(from, to) -> Fetcher

Adds a sort range to the fetcher. Note that the first index is 1. from-to is a closed interval, that means fetcher.range(1,10) gives you up to ten matching states/methods.

fetcher.descending() -> Fetcher

Adds a sort descending rule to the fetcher.

fetcher.ascending() -> Fetcher

Adds a sort ascending rule to the fetcher.

fetcher.unfetch() -> Promise

Unfetches (removes) the Fetcher. callbacks is optional.

// setup some fetcher
var fetcher = new jet.Fetcher();

fetcher.on('data', function(path, event, value) {
  if (event === 'remove') {
    this.unfetch();
  }
});

peer.fetch(fetcher);

The returned Promise may be rejected with:

jet.Method

new jet.Method(path, [access])

Creates and returns a Jet Method. To register the Method at the Daemon call peer.add(method).

  • path: {String} The unique path of the Method
  • access: {Object, Optional} Containing fetchGroups and callGroups
var greet = new jet.Method('greet', {
    fetchGroups: ['public'],
	callGroups: ['public']
});

peer.add(greet).then(function() {
});

method.on('call', cb)

Installs a call event handler, which gets executed whenever some peer issues a call request (peer.call).

The arguments to the call Function are the forwarded "args" field from of original "call" Request. Either an Array or an Object.

The call method can return anything or throw an Error (String/JSON-RPC error) if required.

greet.on('call', function(who) {
    if (who.first === 'John') {
      throw 'John is dismissed';
    }
    var greeting = 'Hello Mr. ' + who.last;
    console.log(greeting);
    return greeting;
});

To provide an async call event handler, provide two arguments to the callback.

  • The forwarded args (Array or Object)
  • reply: {Function} Method for sending the result/error.
greet.on('call', function(who, reply) {
    if (who.first === 'John') {
      throw 'John is dismissed';
    }
    setTimeout(function() {
      var greeting = 'Hello Mr. ' + who.last;
      console.log(greeting);
      reply({
        result: greeting
      });
    }, 100);
});

The arguments to reply can be:

  • result: {Truish, Optional} Operation was success
  • error: {String/Error, Optional} Operation failed

method.add() -> Promise

Register the method from the Daemon convenience function. peer.add(method) must have been called before to initially bind the method to the respective peer!

The returned Promise may be rejected with:

method.remove() -> Promise

Unregister the method from the Daemon. Is the same as calling peer.remove(method).

The returned Promise may be rejected with:

Errors

All Peer methods which return a Promise maybe rejected with a typed error. See the doc of each method to see, which error types this can be for each respective method. For instance the peer.add method may throw jet.ConnectionClosed, jet.Unauthorized or jet.Occupied.

There are three recommended strategies for handling different errors:

  • Using err.name to distinguish between the types
  • Using instanceof operator
// by err.name
peer.set('foo', {age: 3, name: 'bar'})
	.then(function() {})
	.catch(function(err) {
	  if (err.name === 'NotFound') {
	    console.log('foo was not found');
	  } else if (err.name === 'PeerTimeout') {
	    console.log('foo is not responding fast enough');
	  } ...
	});
// by instanceof
peer.set('foo', {age: 3, name: 'bar'})
	.then(function() {})
	.catch(function(err) {
	  if (err instanceof jet.NotFound) {
	    console.log('foo was not found');
	  } else if (err instanceof jet.PeerTimeout) {
	    console.log('foo is not responding fast enough');
	  } ...
	});

jet.BaseError

Base class for all jet Error types. For all error instances err instanceof Error and err instanceof jet.BaseError is true.

jet.ConnectionClosed

The connection to the specified endpoint could not be established or has been closed.

jet.InvalidUser

The user (name) provided is not registered at the Daemon.

jet.InvalidPassword

The password provided for the user is not correct.

jet.NotFound

The State or Method specified by path has not been added to the Daemon. One could fetch the respective State or Method to wait until it becomes available.

jet.Occupied

A State or Method with the specified path can not be added, because another State/Method with the same path already has been added.

jet.PeerTimeout

The Peer processing the set or get request has not answered within the specified timeout.

jet.PeerError

The Peer processing the set ot get request threw an error during dispatching the request.

jet.FetchOnly

The State specified by path cannot be changed. Some States have strict "monitor" characteristics, as they can be observed (by fetched), but not changed by calling peer.set.

jet.Unauthorized

The peer instance (user/password) is not authorized to perform the requested action.