limitReached(token) {
     if (this.blacklist.has(token)) return Promise.resolve(null);
     else {
         return this.getBucket(token).then((limit) =>  {
             if (limit) return Promise.resolve(limit.left > 0);
             return Promise.resolve(null);
         });
     }            
 }
 getLimit(token) {
     if (this.blacklist.has(token)) return Promise.resolve(null);
     else {
         return this.getBucket(token).then((bucket) =>  {
             if (bucket) return Promise.resolve(bucket.getInfo());
             return Promise.resolve(null);
         });
     }            
 }
        getBucket(token) {
            if (this.cache.has(token)) return Promise.resolve(this.cache.get(token));
            else {

                if (this.loading.has(token)) {
                    return new Promise((resolve, reject) => {
                        this.loading.get(token).push({
                              resolve: resolve
                            , reject: reject
                        });
                    });
                }
                else {
                    this.loading.set(token, []);

                    // get the config from the db
                    return this.db.rateLimit('*').getApp().getAccessToken({
                        token: token
                    }).findOne().then((rateLimit) => {
                        if (rateLimit) {
                            let bucket = new LeakyBucket(rateLimit.credits, rateLimit.interval, 0);

                            // initialize from last stored value
                            if (rateLimit.currentValue) {
                                bucket.left = rateLimit.currentValue;
                                bucket.last = rateLimit.updated.getTime();
                            }

                            this.cache.set(token, bucket);

                            // return to others
                            this.loading.get(token).forEach((item) => item.resolve(bucket));
                            this.loading.delete(token);

                            // return to caller
                            return Promise.resolve(bucket);
                        }
                        else {
                            this.blacklist.set(token, true);

                            // return to others
                            this.loading.get(token).forEach((item) => item.resolve());
                            this.loading.delete(token);

                            return Promise.resolve();
                        }
                    }).catch((err) => {
                         this.loading.get(token).forEach((item) => item.reject(err));
                         this.loading.delete(token);

                        return Promise.reject(err);
                    });
                }
            }
        }
        pay(token, cost) {
            if (!this.blacklist.has(token)) {
                return this.getBucket(token).then((bucket) =>  {
                    if (bucket) {
                        bucket.pay(cost);

                        // store the upate
                        this.updatedTokens.set(token, bucket.getInfo().left);

                        return Promise.resolve(bucket.getInfo());
                    }
                    return Promise.resolve(null);
                });
            } else return Promise.resolve(null);
        }