Use external promise based library between sync server side method calls SOLVED


#1

Hi,

I tried different things, but can’t get the code below to run synchroniously.

Step1
step2
step3 (in which I call an external module, which uses promises, code below)
step 4 (publish)

What you see in the log is that meteor does not wait for step 3 to finish

the error

How can I run step 3 in sync mode? thanks

My steps code

var newAppId = copyApp(templateApp.id, customer.name + ' - ' + templateApp.name);
    reloadAppAndReplaceScriptviaEngine(newAppId, '');
    var publishedAppId = publishApp(newAppId, templateApp.name, streamId, customer.name);

the step3 promise based module. The last thing I tried was using async

async function reloadAppAndReplaceScriptviaEngine(docId, scriptReplace) {
    console.log('server: QSSOCKS reloadAppviaEngine');
    //source loic: https://github.com/pouc/qlik-elastic/blob/master/app.js
    var scriptMarker = '§search_terms§';

    return await qsocks.Connect(engineConfig)
        .then(function(global) {
            return global.openDoc(docId);
        })
        .then(function(doc) {
            console.log('** getAppsViaEngine, QSocks opened and now tries to set the script for docID: ', docId);
            return doc.getScript()
                .then(function(script) {
                    // if you want to replace the database connection per customer use the script below.
                    //return doc.setScript(script.replace(scriptMarker, scriptReplace)).then(function (result) {
                    return doc.setScript(script) //we now just include the old script in this app
                        .then(function(result) {
                            console.log('Script replaced');
                            return doc;
                        })
                });
        })
        .then(function(doc) {
            return doc.doReload()
                .then(function(result) {
                    console.log('Reload : ' + result);
                    return doc.doSave()
                        // .then(function(result) {
                        //     console.log('Save : ', result);
                        //     return doc;
                        // });
                })
        })
        .catch((error) => {
            console.error('ERROR while reloading the new app: ',error);
        });
}

#2

Should I use this?

and as such remove the .then ().then() chain? and flatten it?

Or put step3 in code like https://ponyfoo.com/articles/understanding-javascript-async-await

async function asyncFun () {
var value = await Promise
.resolve(1)
.then(x => x * 3)
.then(x => x + 5)
.then(x => x / 2);
return value;
}


#3

no, does not work. @robfallows do you maybe see the error?

function generateAppForTemplate(templateApp, customer) {
    console.log(templateApp);
    console.log('############## START CREATING THE TEMPLATE ' + templateApp.name + ' FOR THIS CUSTOMER: ' + customer.name);
    var streamId = checkStreamStatus(customer) //create a stream for the customer if it not already exists    
    var newAppId = copyApp(templateApp.id, customer.name + ' - ' + templateApp.name);
    reloadAppAndReplaceScriptviaEngine(newAppId, '');
    var publishedAppId = publishApp(newAppId, templateApp.name, streamId, customer.name);
    console.log('############## FINISHED CREATING THE TEMPLATE ' + templateApp.name + ' FOR THIS CUSTOMER: ' + customer.name);

    GeneratedResources.insert({
        'customer': customer.name,
        'streamId': streamId,
        'appId': newAppId
    })
    Meteor.call('updateLocalSenseCopy');
};


//Example to demo that you can also use the Engine API to get all the apps, or reload an app, set the script etc.
async function reloadAppAndReplaceScriptviaEngine(docId, scriptReplace) {
    console.log('server: QSSOCKS reloadAppviaEngine');

    //source loic: https://github.com/pouc/qlik-elastic/blob/master/app.js
    var scriptMarker = '§search_terms§';

    return await qsocks.Connect(engineConfig)
        .then(function(global) {
            return global.openDoc(docId);
        })
        .then(function(doc) {
            console.log('** getAppsViaEngine, QSocks opened and now tries to set the script for docID: ', docId);
            return doc.getScript()
                .then(function(script) {
                    // if you want to replace the database connection per customer use the script below.
                    //return doc.setScript(script.replace(scriptMarker, scriptReplace)).then(function (result) {
                    return doc.setScript(script) //we now just include the old script in this app
                        .then(function(result) {
                            console.log('Script replaced');
                            return doc;
                        })
                });
        })
        .then(function(doc) {
            return doc.doReload()
                .then(function(result) {
                    console.log('Reload : ' + result);
                    return doc.doSave()
                        .then(function(result) {
                            console.log('Save : ', result);
                            return doc;
                        });
                })
        })
        .catch((error) => {
            console.error('ERROR while reloading the new app: ', error);
        });
}


#4

It looks like reloadAppAndReplaceScriptviaEngine returns a Promise. So I think you need to change the way you call it to something like:

  reloadAppAndReplaceScriptviaEngine(newAppId, '')
    .then(function (doc) {
      var publishedAppId = publishApp(newAppId, templateApp.name, streamId, customer.name);
      console.log('############## FINISHED CREATING THE TEMPLATE ' + templateApp.name + ' FOR THIS CUSTOMER: ' + customer.name);

      GeneratedResources.insert({
        'customer': customer.name,
        'streamId': streamId,
        'appId': newAppId,
      });
      Meteor.call('updateLocalSenseCopy');
    });

#5

Cool, off course… but I thought I could write it in a sync manner because of the async await?


#6

You can only use await inside an async function.


#7

Thank you Rob.

There is one problem with the code below. It does not run in sync mode. In the top you see a function that iterates, do something for each customer.

Everything should run after the previous step. So the current code does not “await” …

export function generateStreamAndApp(customers) {
    console.log('METHOD called: generateStreamAndApp for the template apps as stored in the database of the fictive OEM');

    var templateApps = checkTemplateAppExists(); //is a template app selected, and does the guid still exist in Sense? if yes, return the valid templates
    checkCustomersAreSelected(customers); //have we selected a  customer to do the generation for?

    customers.forEach(function(customer) {
        templateApps.forEach(function(templateApp) {
            generateAppForTemplate(templateApp, customer);
        })
    });
};

async function generateAppForTemplate(templateApp, customer) {
    console.log(templateApp);
    console.log('############## START CREATING THE TEMPLATE ' + templateApp.name + ' FOR THIS CUSTOMER: ' + customer.name);

    try {
        var streamId = checkStreamStatus(customer) //create a stream for the customer if it not already exists    
        var newAppId = copyApp(templateApp.id, customer.name + ' - ' + templateApp.name);

        var result = await reloadAppAndReplaceScriptviaEngine(newAppId, '');

        var publishedAppId = publishApp(newAppId, templateApp.name, streamId, customer.name);
        console.log('############## FINISHED CREATING THE TEMPLATE ' + templateApp.name + ' FOR THIS CUSTOMER: ' + customer.name);
    } catch (err) {
        console.error(err);
    }
    GeneratedResources.insert({
        'customer': customer.name,
        'streamId': streamId,
        'appId': newAppId
    });
    Meteor.call('updateLocalSenseCopy');
    return;
};


//Example to demo that you can also use the Engine API to get all the apps, or reload an app, set the script etc.
function reloadAppAndReplaceScriptviaEngine(appId, scriptReplace) {
    console.log('server: QSSOCKS reloadAppviaEngine');

    //source based on loic's work: https://github.com/pouc/qlik-elastic/blob/master/app.js
    var scriptMarker = '§dummyDatabaseString§';
    var _global = {};

    engineConfig.appname = appId; //(String) Scoped connection to app. see https://github.com/mindspank/qsocks
    console.log('Connect to Engine with a new appname parameter when you call global,openDoc: ',engineConfig.appname);

    return qsocks.Connect(engineConfig)
        .then(function(global) {
            console.log('connected to Qsocks');
            _global = global;
            return global.openDoc(appId,'','','',true) //global.openDoc(appId)
        })
        .then(function(doc) {
            console.log('** getAppsViaEngine, QSocks opened and now tries to set the script for appId: ', appId);
            return doc.getScript()
                .then(function(script) {
                    // if you want to replace the database connection per customer use the script below.
                    //return doc.setScript(script.replace(scriptMarker, scriptReplace)).then(function (result) {
                    //you can also change the sense database connection: https://github.com/mindspank/qsocks/blob/master/examples/App/create-dataconnection.js
                    return doc.setScript(script) //we now just include the old script in this app
                        .then(function(result) {
                            console.log('Script replaced');
                            return doc;
                        })
                });
        })
        .then(function(doc) {
            return doc.doReload()
                .then(function(result) {
                    console.log('Reload : ' + result);
                    return doc.doSave()
                        .then(function(result) {
                            console.log('Save : ', result);
                            _global.connection.close();
                            return doc;
                        });
                })
        })
        .catch((error) => {
            console.error('ERROR while reloading the new app: ', error);
            throw new Meteor.error(error);
        });
}

you see it should be
shell (step 1,2 3)
esso (Step 1 2 3)

but it does not wait for the first customer 1 to finish


#8

Hi, @robfallows Thanks again for your help. I have the code working in sync mode now with the code below.

I just call a async function reloadApp, which includes a return await promise.then...then..then

export function generateStreamAndApp(customers) {
    console.log('METHOD called: generateStreamAndApp for the template apps as stored in the database of the fictive OEM');

    var templateApps = checkTemplateAppExists(); //is a template app selected, and does the guid still exist in Sense? if yes, return the valid templates
    checkCustomersAreSelected(customers); //have we selected a  customer to do the generation for?
    for (const customer of customers) {
            for (const templateApp of templateApps) {
            generateAppForTemplate(templateApp, customer);
        }
    };
};

function generateAppForTemplate(templateApp, customer) {
    console.log(templateApp);
    console.log('############## START CREATING THE TEMPLATE ' + templateApp.name + ' FOR THIS CUSTOMER: ' + customer.name);

    try {
        var streamId = checkStreamStatus(customer) //create a stream for the customer if it not already exists    
        var newAppId = copyApp(templateApp.id, customer.name + ' - ' + templateApp.name);
        var result =  reloadAppAndReplaceScriptviaEngine(newAppId, '');
        var publishedAppId = publishApp(newAppId, templateApp.name, streamId, customer.name);
        console.log('############## FINISHED CREATING THE TEMPLATE ' + templateApp.name + ' FOR THIS CUSTOMER: ' + customer.name);
    } catch (err) {
        console.error(err);
    }
    GeneratedResources.insert({
        'customer': customer.name,
        'streamId': streamId,
        'appId': newAppId
    });
    Meteor.call('updateLocalSenseCopy');
    return;
};


//Example to demo that you can also use the Engine API to get all the apps, or reload an app, set the script etc.
async function reloadAppAndReplaceScriptviaEngine(appId, scriptReplace) {
    console.log('server: QSSOCKS reloadAppviaEngine');

    //source based on loic's work: https://github.com/pouc/qlik-elastic/blob/master/app.js
    var scriptMarker = '§dummyDatabaseString§';
    var _global = {};

    engineConfig.appname = appId; //(String) Scoped connection to app. see https://github.com/mindspank/qsocks
    console.log('Connect to Engine with a new appname parameter when you call global,openDoc: ', engineConfig.appname);

    return await qsocks.Connect(engineConfig)
        .then(function(global) {
            console.log('connected to Qsocks');
            _global = global;
            return global.openDoc(appId, '', '', '', true) //global.openDoc(appId)
        })
        .then(function(doc) {
            console.log('** getAppsViaEngine, QSocks opened and now tries to set the script for appId: ', appId);
            return doc.getScript()
                .then(function(script) {
                    // if you want to replace the database connection per customer use the script below.
                    //return doc.setScript(script.replace(scriptMarker, scriptReplace)).then(function (result) {
                    //you can also change the sense database connection: https://github.com/mindspank/qsocks/blob/master/examples/App/create-dataconnection.js
                    return doc.setScript(script) //we now just include the old script in this app
                        .then(function(result) {
                            console.log('Script replaced');
                            return doc;
                        })
                });
        })
        .then(function(doc) {
            return doc.doReload()
                .then(function(result) {
                    console.log('Reload : ' + result);
                    return doc.doSave()
                        .then(function(result) {
                            console.log('Save : ', result);
                            _global.connection.close();
                            return doc;
                        });
                })
        })
        .catch((error) => {
            console.error('ERROR while reloading the new app: ', error);
            throw new Meteor.error(error);
        });
}