Code coverage report for node_acl/lib/mongodb-backend.js

Statements: 78.82% (67 / 85)      Branches: 55.88% (19 / 34)      Functions: 86.67% (26 / 30)      Lines: 92.65% (63 / 68)      Ignored: none     

All files » node_acl/lib/ » mongodb-backend.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168            1 1 1   1 1 1     1           42             42 42 42                                         66     66 66 66 66 66 56                 139       139 139 139 139 139 63 63 69   63                 72       72 72 72 72 72 72     72 112     72 72 72                   19     19 19 19 19 19 19 19 19                   22       22 22 22 22 22     22 22     22 22 22             1 113     1  
/**
  MongoDB Backend.
  Implementation of the storage backend using MongoDB
*/
"use strict";
 
var contract = require('./contract');
var async = require('async');
var _ = require('lodash');
 
function MongoDBBackend(db, prefix){
  this.db = db;
  this.prefix = typeof prefix !== 'undefined' ? prefix : '';
}
 
MongoDBBackend.prototype = {
 /**
     Begins a transaction.
  */
  begin : function(){
    // returns a transaction object(just an array of functions will do here.)
    return [];
  },
 
  /**
     Ends a transaction (and executes it)
  */
  end : function(transaction, cb){
    contract(arguments).params('array', 'function').end();
    async.series(transaction,function(err){
      cb(err instanceof Error? err : undefined);
    });
  },
 
  /**
    Cleans the whole storage.
  */
  clean : function(cb){
    contract(arguments).params('function').end();
    this.db.collections(function(err, collections) {
      if (err instanceof Error) return cb(err);
      async.forEach(collections,function(coll,innercb){
        coll.drop(function(){innercb()}); // ignores errors
      },cb);
    });
  },
 
  /**
     Gets the contents at the bucket's key.
  */
  get : function(bucket, key, cb){
    contract(arguments)
        .params('string', 'string', 'function')
        .end();
    this.db.collection(this.prefix + bucket,function(err,collection){
      Iif(err instanceof Error) return cb(err);
      collection.findOne({key:key},function(err, doc){
        Iif(err) return cb(err);
        if(! _.isObject(doc) ) return cb(undefined,[]);
        cb(undefined,_.without(_.keys(doc),"key","_id"));
      });
    });
  },
 
  /**
    Returns the union of the values in the given keys.
  */
  union : function(bucket, keys, cb){
    contract(arguments)
      .params('string', 'array', 'function')
      .end();
 
    this.db.collection(this.prefix + bucket,function(err,collection){
      Iif(err instanceof Error) return cb(err);
      collection.find({key: { $in: keys }}).toArray(function(err,docs){
        Iif(err instanceof Error) return cb(err);
        if( ! docs.length ) return cb(undefined, []);
        var keyArrays = [];
        docs.forEach(function(doc){
          keyArrays.push.apply(keyArrays, _.keys(doc));
        });
        cb(undefined, _.without(_.union(keyArrays),"key","_id"));
      });
    });
  },
 
  /**
    Adds values to a given key inside a bucket.
  */
  add : function(transaction, bucket, key, values){
    contract(arguments)
        .params('array', 'string', 'string','string|array')
        .end();
 
    Iif(key=="key") throw new Error("Key name 'key' is not allowed.");
    var self=this;
    transaction.push(function(cb){
      values = makeArray(values);
      self.db.collection(self.prefix + bucket, function(err,collection){
        Iif(err instanceof Error) return cb(err);
 
        // build doc from array values
        var doc = {};
        values.forEach(function(value){doc[value]=true;});
 
        // update document
        collection.update({key:key},{$set:doc},{safe:true,upsert:true},function(err){
          Iif(err instanceof Error) return cb(err);
          cb(undefined);
        });
      });
    });
  },
 
  /**
     Delete the given key(s) at the bucket
  */
  del : function(transaction, bucket, keys){
    contract(arguments)
        .params('array', 'string', 'string|array')
        .end();
    keys = makeArray(keys);
    var self= this;
    transaction.push(function(cb){
      self.db.collection(self.prefix + bucket,function(err,collection){
        Iif(err instanceof Error) return cb(err);
        collection.remove({key:{$in:keys}},{safe:true},function(err){
          Iif(err instanceof Error) return cb(err);
          cb(undefined);
        });
      });
    });
  },
 
  /**
    Removes values from a given key inside a bucket.
  */
  remove : function(transaction, bucket, key, values){
    contract(arguments)
        .params('array', 'string', 'string','string|array')
        .end();
 
    var self=this;
    values = makeArray(values);
    transaction.push(function(cb){
      self.db.collection(self.prefix + bucket,function(err,collection){
        Iif(err instanceof Error) return cb(err);
 
        // build doc from array values
        var doc = {};
        values.forEach(function(value){doc[value]=true;});
 
        // update document
        collection.update({key:key},{$unset:doc},{safe:true,upsert:true},function(err){
          Iif(err instanceof Error) return cb(err);
          cb(undefined);
        });
      });
    });
  }
}
 
function makeArray(arr){
  return Array.isArray(arr) ? arr : [arr];
}
 
exports = module.exports = MongoDBBackend;