0

I am quite new to Node.js and I am trying to load json configs files stored either in Amazon s3 or local repository. Below is my code so far:

var cfg = process.env.CONFIG_FILE_NAME;

log.info("Loading config '%s'", cfg);

if(cfg.indexOf("s3") !== -1 || cfg.indexOf("S3") !== -1) {
    log.info("S3 path detected");
    var s3 = new aws.S3();
    var myRegex = /\/\/(\w*)\/(.*)/g;
    var matched = myRegex.exec(cfg);
    var bucket = matched[1];
    log.info("Extracted bucket: ", bucket);
    var key = matched[2];
    log.info("Extracted key: ", key);

    var params = {
      Bucket: bucket, 
      Key: key
    };
    s3.getObject(params, function(err, data) {
      if (err) log.warn(err, err.stack);
      else {
          log.info("Loaded config from S3");
          cfg = JSON.parse(data.Body);
          log.info("Config content: "cfg);
      }
    });

}
else {
     try {       
          //some code here      
        } catch (e) {
         //some code here
        }

}


subscriptions = cfg.subscriptions;
log.info("This supposes to contain json content from S3: ", cfg);

The idea is that the code will check if there is a path to S3 in the message sent to Amazon Lambda (CONFIG_FILE_NAME field). If it exists, then the code load the config file from s3, otherwise, it loads locally. However, when I try to run the code, it returns something like this:

4 Jan 11:37:34 - [INFO] Loading config 'Path-to-S3'

4 Jan 11:37:34 - [INFO] S3 path detected

4 Jan 11:37:34 - [INFO] Extracted bucket: mybucket

4 Jan 11:37:34 - [INFO] Extracted key: mykey.cfg.json

4 Jan 11:37:34 - [INFO] "This suppose to contain json content from S3: Path-to-S3'

4 Jan 11:37:34 - [INFO] Loaded config from S3

4 Jan 11:37:34 - [INFO] Config content: my-config-content

So the problem is that, the code executes the line subscriptions = cfg.subscriptions; before the config file is loaded from S3. The variable cfg at this line only contains the path to the config, not the config content I want to load from S3. My later code implementation depends on this subscriptions field from cfg file so it stucks right here.

dondon
  • 105
  • 1
  • 1
  • 10
  • 1
    Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Brahma Dev Jan 04 '18 at 11:24
  • All of the code which depends on variable `cfg` has to go in the callback. This is not wrong order. This is how node works. Code in callback is asynchronous – ilmirons Jan 04 '18 at 11:24
  • wrap the snippet in Promise and call resolve in `getObject` . probably the simplest thing you can do. – Gntem Jan 04 '18 at 11:25
  • Please read tutorial about the nature of nodejs [here](https://blog.risingstack.com/node-hero-async-programming-in-node-js/) – Manjeet Thakur Jan 04 '18 at 11:27
  • One thing I don't quite understand is that when I try to load the config file locally with cfg = require("./" + cfg); (in block Else) The code executes in the order I wanted. The variable "subscriptions" can get the exact value from the cfg file. Can anyone explain this to me ? Does the code depend on the reading speed (reading locally is faster than loading from s3) ? – dondon Jan 04 '18 at 16:20

1 Answers1

0

You can use async module to make your code work. (npm install --save async)

Async is a utility module which provides straight-forward, powerful functions for working with asynchronous JavaScript.

var async = require("async");
//or you can just use var waterfall = require("async/waterfall");
var cfg = process.env.CONFIG_FILE_NAME;
log.info("Loading config '%s'", cfg);
async.waterfall([
    function (callback) {
        if (cfg.indexOf("s3") !== -1 || cfg.indexOf("S3") !== -1) {
            log.info("S3 path detected");
            var s3      = new aws.S3();
            var myRegex = /\/\/(\w*)\/(.*)/g;
            var matched = myRegex.exec(cfg);
            var bucket  = matched[1];
            log.info("Extracted bucket: ", bucket);
            var key = matched[2];
            log.info("Extracted key: ", key);
            var params = {
                Bucket: bucket,
                Key   : key
            };
            s3.getObject(params, function (err, data) {
                if (err) {
                    log.warn(err, err.stack);
                    callback();
                }
                else {
                    log.info("Loaded config from S3");
                    cfg = JSON.parse(data.Body);
                    log.info("Config content: ", cfg);
                    callback(null, cfg);
                }
            });
        }
        else {
            callback();
        }
    },
    function (cfg, callback) {
        try {
            //some code here
        }
        catch (e) {
            //some code here
        }
        var subscriptions = cfg.subscriptions;
        log.info("This supposes to contain json content from S3: ", cfg);
        callback(null, 'done');
    }
], function (err, result) {
    // result now equals 'done'
});
Ratan Kumar
  • 1,640
  • 3
  • 25
  • 52
  • Thanks for the suggestion, I tried your code and it worked for that block of code (block A). I have a further question on this, now I have another block of code (block B) which will read the "subscriptions" value from block A. However, currently block B executes before block A can return the value of "subscriptions" so in the end block B gets nothing to process. How should I force block B to run only after block A finished? – dondon Jan 04 '18 at 16:38
  • read about async.waterfall you can add block B second function which will surly run after block a has ran. http://caolan.github.io/async/docs.html#waterfall – Ratan Kumar Jan 04 '18 at 19:50