Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda nodeJS 4.3 not finishing/executing success callback

Despite running the log statement immediately above it, my call to callback(null) isn't working. Even tried wrapping it in a try catch block but got nothing.

For reference, here's the full function:

var Firebase = require('firebase');
var request = require('request');

//noinspection AnonymousFunctionJS
/**
 *
 * @param event - from Lambda
 * @param context - from Lambda
 * @param callback - from Lambda
 */
exports.handler = function (event, context, callback) {
    var AUTOPILOT_API_KEY = getKey(event.stage, 'AUTOPILOT_API_KEY');
    var AUTOPILOT_JOURNEY_ID = getKey(event.stage, 'AUTOPILOT_JOURNEY_ID');
    activate();

    function activate () {
        console.log('event:', event);

        if (validPayload(event, context)) {
            addDefaultPresets(event.uid);
            addToAutopilot(event.user, event.uid);
        }
    }

    /**
     * checks that the necessary payload has been received
     * if YES: returns true and allows process to continue
     * if NO: throws context.fail with useful error message(s)
     * operating under custom error code naming convention of
     * http code + 3 digit ULM error code
     * @param event - from Lambda
     * @param context - from Lambda
     * @returns {boolean} - whether the payload contains the required data
     */
    function validPayload (event, context) {
        return true; // REDACTED FOR BREVITY
    }

    /**
     * Adds the user to Autopilot as a contact and adds them
     * to the journey with trigger id 0001
     * @param {Object} user
     * @param {string} uid generate by Firebase as unique identifier of registered user
     */
    function addToAutopilot (user, uid) {

        // REDACTED FOR BREVITY

        request({
            method: 'POST',
            url: 'https://api2.autopilothq.com/v1/trigger/' + AUTOPILOT_JOURNEY_ID + '/contact',
            headers: {
                'autopilotapikey': AUTOPILOT_API_KEY,
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(payload)
        }, function (error, response, body) {
            //noinspection MagicNumberJS
            if (response.statusCode !== 200) {
                errorResponse.status = response.statusCode;
                errorResponse.error = {
                    errorMessage: error,
                    user: event.user,
                    response: response,
                    body: body
                };
                console.log('should throw ERROR callback');
                context.fail(JSON.stringify(errorResponse));
            } else {
                console.log('should throw SUCCESS callback');
                console.log(JSON.stringify({
                    status: response.statusCode,
                    message: "User successfully added to Autopilot account & journey"
                }));

                callback(null);
            }
            console.log('Finished addToAutopilot()');
        });
    }

    /**
     * Adds a collection of presets the the account of the new user
     * @param uid {String} - Firebase UID
     */
    function addDefaultPresets (uid) {
        // REDACTED FOR BREVITY

        var presets = ref.child('users').child(uid).child('presets');

        console.log('Starting addDefaultPresets()');

        activate();

        function activate () {
            console.info('activating...');
            // for each field
            fields.forEach(function (field) {
                // iterate over each preset
                presetData[field].forEach(function (label) {
                    // and add to firebase db via addDefaultPreset() if unique
                    presetIsUnique(field, label);
                })
            });

            console.log('Finished addDefaultPresets()');
        }

        function presetIsUnique (field, label) {
            presets.child(field).orderByChild('label')
                .equalTo(label)
                .once('value', function (snapshot) {
                    var val = snapshot.val();
                    if (!val) {
                        addDefaultPreset(field, label);
                    } else {
                        console.error('already exists', field, label);
                    }
                });
        }

        function addDefaultPreset (field, label) {
            //noinspection MagicNumberJS
            presets.child(field).push().set({
                creator: 'default',
                dateAdded: Math.floor(Date.now() / 1000),
                label: label
            }, setCallback);
        }

        function setCallback (err) {
            if (err) {
                console.error(err);
            } else {
                console.info('added preset');
            }
        }
    }

    function getKey (stage, keyId) {
        var keys = {
            AUTOPILOT_API_KEY: {
                staging: 'dev123',
                prod: 'prod123'
            },
            AUTOPILOT_JOURNEY_ID: {
                staging: 'XXX',
                product: 'XXXX'
            },
            FIREBASE_URL: {
                staging: 'https://staging.firebaseio.com/',
                prod: 'https://prod.firebaseio.com/'
            }
        };

        if (stage === 'prod') {
            return keys[keyId][stage];
        } else {
            return keys[keyId]['staging'];
        }
    }
};
like image 871
Bryce York Avatar asked Feb 12 '26 11:02

Bryce York


2 Answers

It seems like firebase is keeping something in the event loop. By default, lambda waits until the event loop is empty before terminating. You can set context.callbackWaitsForEmptyEventLoop = false to tell lambda to terminate the function soon after the callback is called, even if there is still items in the event loop.

like image 72
Jonathan Seed Avatar answered Feb 15 '26 11:02

Jonathan Seed


After hours and hours of debugging here is something that worked for me:

var firebase = require('firebase');
var app = firebase.initializeApp(config);

...
...
...

app.delete().then(function(){
   var response = {
      statusCode: 200,
      body: JSON.stringify({
         message: 'Success'
      }),
   };
   callback(null, response);
});

You can read more about delete in the official documentation:

Make the given App unusable and free the resources of all associated services.

Hope this is helpful!

like image 39
temkin Avatar answered Feb 15 '26 12:02

temkin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!