NodeJS API


Purpose:
 Short description of key concepts in Datanet and Datanet's simplest API:
 command line interface API.

SECTION ONE: Key Concepts

  The Datanet has 4 dimensions: [Users, Devices, Channels, Documents].

  Basics:
    1.) Users have login/password authentication.
    2.) Devices are physical machines (e.g. app-servers or mobile-phones).
    3.) Users are stationed on Devices.
    4.) Users subscribe to Channels.
    5.) Channels are pub/sub channels publising Documents.
    6.) Documents contain data and metadata.
    7.) A Document's metadata specifies Channels to be published to.
    8.) A User can CACHE a Document on its current Device.
    9.) Replication is accomplished by either:
        A.) publishing Documents on Channels to subscribers on
            different Devices
        B.) sending Document-modifications to different Devices CACHING
            the Document
    10.) Documents are stored in collections
    11.) Collections are grouped into namespaces
    12.) An individual document is addressable as:
           Namespace_name.Collection_name.Document_key


  Details:
    1.) AdminUsers grant priviliges on Channels to NormalUsers.
    2.) NormalUsers subscribe to Channels (on which they have appropriate
        priviliges).
    3.) A NormalUser (referred to as User) can subscribe to multiple Channels.
    4.) Applications (e.g. mobile-apps or app-server-apps) station Users on
        Devices the application is running on
    5.) A Device can have multiple Users stationed on it, mobile-devices
        usually only have one User stationed on them.
    6.) A Document contains data.
    7.) A Document also contains metadata specifying to which Channel(s) it
        will be published.
    8.) A User creating, modifying, or removing a Document must have WRITE
        priviliges on ALL Channels specified in the Document's metadata.
    9.) A Document can specify multiple Channels to replicate to, although one
        Channel per Document is the norm.
    10.) A User can direct the system to CACHE a Document on the User's
         current Device
    11.) CACHED Documents are subject to cache-eviction, PubSub documents
         are not

SECTION TWO: NodeJS API

  The following is a list of commands Datanet's NodeJS Agent supports and
  examples on how to use them.

  PART-ONE: Admin commands
    1.1.) Client.AddUser(username, password, callback)
      Purpose:
        Client.AddUser() command adds a user with password to Datanet
      Notes:
        * Only Admin users can call Client.AddUser()
        * Username must be unique
        * ROLE can be [ADMIN,USER]
        * callback function has args [error, status]
      Example:
        Add normal user 'User2' w/ password 'MyPass' to Datanet
          Client.AddUser('User2', 'MyPass', callback);

    1.2.) Client.GrantUser(username, privilege, channel_name, callback)
      Purpose:
        Client.GrantUser() command grants a user priviliges to a channel
      Notes:
        * Only Admin users can call Client.GrantUser()
        * Privliges can be [READ,WRITE]
        * callback function has args [error, status]
      Example:
        Grant 'User3' WRITE priviliges on channel 'ChanUser3'
          Client.GrantUser('User3', 'WRITE', 'ChanUser3', callback);

  PART-TWO: User commands
    2.1.) Client.SwitchUser(authorization, callback)
      Purpose:
        Client.SwitchUser() command switches client's current user
      Notes:
        * The User will be authenticated
        * 'authorization' requires 'username' & 'password' fields
        * callback function has args [error, status]
      Example:
        Switch to 'User2' w/ password 'MyPass'
          var auth = {username : 'User2', password : 'MyPass'};
          Client.SwitchUser(auth, callback);

    2.2.) Client.StationUser(authorization, callback)
      Purpose:
        Client.StationUser() command stations a User on the Agent
      Notes:
        * The User will be authenticated
        * If the User has already subscribed to some channels, ALL the keys in 
          those channels will be asynchronously replicated to this device
        * 'authorization' requires 'username' & 'password' fields
        * callback function has args [error, status]
      Example:
        Station 'User3' w/ password 'SuperPass'
          var auth = {username : 'User3', password : 'SuperPass'};
          Client.StationUser(auth, callback);

    2.4.) Client.Subscribe(channel_name, callback)
      Purpose:
        Client.Subscribe() command subscribes the current user to a
        given Channel
      Notes:
        * The User will be authenticated
        * ALL the keys in this channel will be asynchronously replicated to ALL 
          devices the current user is stationed on
        * callback function has args [error, status]
      Example:
        Subscribe current user to channel: 'ChanUser3'
          Client.Subscribe('ChanUser3', callback);


  PART-THREE: Data-persistence commands
    3.1.) Client.SetNamespace(namespace_name)
      Purpose:
        Client.SetNamespace() command switches client's current namespace
      Notes:
        * The namespace is not validated
        * This is a synchronous call -> no callback
        * this function returns a COLLECTION object
          |-> used in Store, Fetch, etc...
      Example:
        Switch to namespace 'production'
          Client.SetNamespace('production');

    3.2.) Client.Collection(collection_name)
      Purpose:
        Client.Collection() command switches client's current collection
      Notes:
        * The collection is not validated
        * This is a synchronous call -> no callback
      Example:
        Switch to collection 'user_data'
          var collection = Client.Collection('user_data');

    3.3.) Collection.store(data, callback)
      Purpose:
        Collection.store() command persists a data-document to Datanet
      Notes:
        * 'data' must be valid JSON
        * field '_id' is a keyword
        * field '_channels' is a keyword
        * Document will be replicated throughout entire Datanet according to
          the document's channels and which Agents are caching the document
        * User must have write-permissions to ALL channels specified in
          Document's metadata
        * If User is not subscribed to any channels specified in Document's
          metadata but has write permissions on the Document, the Document
          is automatically CACHED (section 3.10) on this Agent
        * callback function has args [error, document]
      Example:
        Store document w/ key: 'MyData', replicating to channel: 'ChanUser3'
          var collection = Client.Collection("users");
          var data       = {'_id'       : 'MyData',
                            '_channels' : ['ChanUser3'],
                            'user_id'   : 3,
                            'firstname' : 'John',
                            'lastname'  : 'Doe',
                            'age'       : 22,
                            'events'    : ['user_registered']}
          Collection.store(data, callback);

    3.4.) Collection.remove(key_name, callback)
      Purpose:
        Collection.remove() command removes a single document from Datanet
      Notes:
        * Document will be removed throughout entire Datanet according to
          its channels
        * User must be subscribed and have write-permissions to ALL channels
          specified in Document's metadata
        * callback function has args [error, status]
      Example:
        Remove document w/ key 'Mydata'
          Collection.remove('Mydata', callback);
  
    3.5.) Collection.fetch(key_name, callback)
      Purpose:
        Collection.fetch() command fetches a key's CRDT
      Notes:
        * FETCH is the first step when modifying data (FETCH->modify->COMMIT)
        * callback function has args [error, document]
      Example:
        Fetch document w/ key 'Mydata'
          Collection.fetch('Mydata', callback);

    3.6.) Collection.commit(callback)
      Purpose:
        Collection.commit() command commits modifications to a document
      Notes:
        * Agent will persist then replicate the commit as a Delta
        * Commits may be rejected (e.g. key was removed)
        * callback function has args [error, document]
      Example:
        Commit modifications to document w/ key 'MyData'
          var collection = Datanet:collection("users");
          collection:fetch('MyData', function(err, doc) {
            -- MODIFY DOCUMENT discussed in PART FOUR
            Collection.commit(callback);
          });

    3.7.) Collection.cache(key_name, callback)
      Purpose:
        Collection.cache() command caches a document on Agent
      Notes:
        * User must have READ or WRITE priviliges on at least one of
          Document's Channels
        * callback function has args [error, document]
      Example:
        Cache document w/ key 'HotData'
          Collection.cache('HotData', callback);

    3.8.) Collection.expire(key_name, callback)
      Purpose:
        Collection.expire() command expires a Document system-wide in X seconds
      Notes:
        * User must have WRITE priviliges on ALL of the Document's Channels
        * Expiration seconds can contain milliseconds (e.g. 5.001 seconds)
        * callback function has args [error, status]
      Example:
        Expire document w/ key 'HotData' in 5.001 seconds
          Collection.expire('HotData', 5.001, callback);


  PART-FOUR: Data-modification commands
    NOTES:
      A.) Data-modificaton commands are applied to the current document, the
          previous document fetched (via Collection.fetch())
      B.) Fields are accessed via dot-notation lookups
          Examples:
          1.) the root-level field '{age:}' is accessed via 'age'
          2.) the nested field '{user:{actions:[]}}'
              is accessed via 'user.actions'
          3.) the nested array element '{user:{actions:[,,,,4]}}'
              is accessed via 'user.actions.4' -> NOTE: starts at ZERO
      C.) Values can be any valid JSON or simple value (INT,STRING)
      D.) Data-modifications will not be persisted or replicated until
          Collection.commit() is called

    4.1.) Document.set(field_name, value)
      Purpose:
        Document.set() command sets a field to a value in the current document
      Notes:
        * if field does not exist it will be created
        * if field does exist it will be overwritten
        * function is synchronous -> no callback
        * function returns errors (if any)
      API:
        SET field value
      Example:
        1.) Set field 'firstname' to value 'BILL'
              var err = doc.set('firstname', 'BILL');
        2.) Set field 'actions' to array with two events
              var err = doc.set('actions', ["registered","logged_in"]);
        3.) Set 3rd element in array 'actions' to 'logged_out'
              var err = doc.set('actions.3', 'logged_out');

    4.2.) Document.delete(field_name)
      Purpose:
        Document.delete() command deletes a field from the current document
      Notes:
        * if field does not exist, DELETE is a no-op
        * function is synchronous -> no callback
        * function returns errors (if any)
      Example:
        1.) Delete field 'limited_time_offer'
          var err = doc.delete('limited_time_offer');

    4.3.) Document.insert(field_name, position, value)
      Purpose:
        Document.insert() command inserts an element into an array at a 
        position in the current document
      Notes:
        * if field does not exist, INSERT will create an empty array
        * if position is greater than the length of the array, null values
          will be added to fill in missing positions
          (SORRY: it's a JSON standard)
        * If field is not an array (e.g an object), an error will be returned
        * function is synchronous -> no callback
        * function returns errors (if any)
      Example:
        1.) Insert 'logged_in' event to first element in
            var err = doc.insert('user_actions', 0, 'logged_in');
        2.) Same use-case but 'logged_in' event is a dictionary and
            array-field is nested field 'user{actions:[]}'
              var event = {"action"    : "logged_in",
                           "timestamp" : 1435775648,
                           "device"    : 456};
              var err    = doc.insert('user.actions', 0, event);

    4.4.) Document.increment(field_name, by_value)
      Purpose:
        Document.increment() command increments a numerical field by a 
        given number in the current document
      Notes:
        * if field does not exist, INCR is a no-op
        * if field is not a numerical field (e.g. string), an error is returned
        * function is synchronous -> no callback
        * function returns errors (if any)
      Example:
        Increment field 'age' by value 1
          var err = doc.increment('age', 1);