Michael Snare
Michael Snare

Reputation: 13

Nested WLJSONStore Calls Not Executing in Expected Sequence and Not Adding Items to the Collection

This is Worklight 6.1 code with dojo, testing with Chrome and the std dev server Liberty. What I want this code to do is to query a collection, which should have 0 or 1 entries, and either retrieve the one entry if it exists or create an entry with a supplied set of values. What I'm trying to do is store a url, id, and password for a service. If this is the first time the app has run after installation I want to prompt the user for this info and store it. The code to prompt the user will be added later. If it is not the first run of the app then the values should be stored in the collection and be retrieved. I'll add code later to allow the user to change and update the values.

What is happening now is that the .add never seems to be executed, and also the execution sequence I'm seeing thru the breakpoints I've set seems weird.

Here is the setup code

        // set up the jsonStore
        var collectionName = 'servers';
        var collections = {};
        collections[collectionName] = {};

        // initialize the default jsonStore Monitor credentials
        var jsonURL = 'http://myserver.com:9082';
        var jsonUser = 'keyser';
        var jsonPassword = 'soze';

And here is the problem code

        // Initialize the JSONStore 
        WL.JSONStore.init(collections)
            .then(function() {
                console.log("store initialized");
                // query the store
                var query = {_id: 0};
                WL.JSONStore.get(collectionName)
                .find(query)
                .then(function(arrayResults) {
                    console.log("credentials retrieved " + arrayResults.length);
                    if (arrayResults.length > 0) {
                        // retrieve the credentials from the json object
                        console.log("password retrieved " + arrayResults[0].json.password);
                        jsonURL = arrayResults[0].json.url;
                        jsonUser = arrayResults[0].json.user;
                        jsonPassword = arrayResults[0].json.password;                           
                    } else {
                        // load the default credentials into jsonStore
                        var credentials = {url: jsonURL, user: jsonUser, password: jsonPassword};
                        WL.JSONStore.get(collectionName) 
                            .add(credentials)
                            .then(function() {
                                console.log("credentials loaded " + credentials.url);
                            })
                            .fail(function(errorObject) {
                                console.log("credential load failed");
                            });                         
                    }  // end of else
                    //  Query the model list
                    queryModels();
                })  // end of get(collectionName) then

                .fail(function(errorObject) {
                    console.log("credentials not retrived");
                });  // end of get(collectionName) fail
            }) // end of init(collections) then

            .fail(function(errorObject) {
                console.log("store init failed" + errorObject);
            }); // end of init(collections) fail

    });  // end of ready

When I step thru it flows in this sequence.

init(collections)

Then it jumps immediately to the "end of ready". Seems weird but I'm a rookie so maybe it's OK?

Back to the get(collectionName)

to the .then and logs "credentials retrieved" with and array length of 0

To the else clause of the statement

And it breaks on the get(collectionName) in the else clause. So far so good

From here it jumps to queryModels(), skipping over the .add (far as I can tell)

Then it returns to the .then under the 2nd get and logs "credentials loaded"

At this point execution ends "normally" except, The item never gets added to the collection, and The queryModels runs before I expect it to, I want it to run after the item is added.

By now it's probably obvious that I'm a rookie, so I'm probably making the rookie mistake. I know I'm dealing with deferreds here with the .then and .fails, and I'm nesting them, which seems to be an accepted technique, but I'm not getting the execution sequence I want.

I've tried this code commenting out the 2nd get(collections) in a couple of formats and it barfs both ways.

                        // WL.JSONStore.get(collectionName) 
                            .add(credentials)

and

                        // WL.JSONStore.get(collectionName) 
                            servers.add(credentials)

Any help greatly appreciated. Thanks!

Here's my "answer" below based on what I learned from the other answers below.

Bluewing and cnandrue's answers were both very helpful, and I got it working. The main issues I had turned out to be.

  1. I had failed to grasp that slot 0 in a collection equates to a document _id key of 1. I was trying to query _id = 0, and never getting a hit. The add to the collection was working all along, I was just not reading it correctly.

  2. Moving the queryModels into the if/else clauses (bluewing's suggestion) worked, and reading the material cnandreu referenced (very worthwhile to read) explained why it worked. Thanks!

  3. The tip about the "weird" execution sequence being an artifact of the breakpoints was also very useful, I quit chasing that red herring.

Here is a working draft of the code after fixing these issues. I did not implement all of the suggestions yet, but probably will as I polish this up. Thanks again.

        // Initialize the JSONStore - you have to .init to start the collection before you can read it.
        WL.JSONStore.init(collections)
            .then(function() {
                console.log("store initialized");
                // query the store
                var query = {_id: 1};
                WL.JSONStore.get(collectionName)    // get 1
                .find(query)
                .then(function(arrayResults) {
                    console.log("credentials retrieved " + arrayResults.length);
                    if (arrayResults.length > 0) {
                        // retrieve the credentials from the json object
                        console.log("password retrieved " + arrayResults[0].json.password);
                        jsonURL = arrayResults[0].json.url;
                        jsonUser = arrayResults[0].json.user;
                        jsonPassword = arrayResults[0].json.password;
                        queryModels();
                    } else {
                        // load the default credentials into jsonStore
                        var credentials = {url: jsonURL, user: jsonUser, password: jsonPassword};
                        WL.JSONStore.get(collectionName)  // get 2
                            .add(credentials)
                                .then(function(numberOfDocumentsAdded) {
                                    console.log("Number of Docs Added" + numberOfDocumentsAdded);
                                    queryModels();
                                });  // end of .add then
                    }  // end of else
                });  // end of get(collectionName) 1 then
            }) // end of init(collections) then

            .fail(function(errorObject) {
                console.log("something failed" + errorObject);
            }); // end of init(collections) fail

Upvotes: 1

Views: 173

Answers (2)

cnandreu
cnandreu

Reputation: 5111

My suggestion is the same as Bluewings', but I wanted to share some pseudocode:

function handleCredentials (arrayResults, callback) {

    if (arrayResults.length > 0) {
        //.... synchronous code here.

        setTimeout(function () {
          callback();
        }, 0);

    } else {

        WL.JSONStore.get(collectionName) 
        .add({url: jsonURL, user: jsonUser, password: jsonPassword})
        .then(function() {
            callback();
        });                  
    }
}

WL.JSONStore.init(collections)
.then(function() {

  WL.JSONStore.get(collectionName)

  .find({_id: 1})

  .then(function (arrayResults) {

    handleCredentials(arrayResults, function () {
      queryModels();
    });

  });

});

Notice I created a function for handleCredentials, that function will either do a synchronous operation (setting some variables with the result from the find call) or an asynchronous operation (calling add to add credentials). A setTimeout with 0 is called to preserve async behavior, this is explained in detail here. After the handleCredentials function has finished, you call the queryModels function via the callback pattern.

As an aside, I recommended reading this blog post: What’s so great about JavaScript Promises?. Especially the "Error Handling" section. You don't need to add a .fail to every promise, you can get away with less failure functions and the error object should provide enough details into what went wrong. JSONStore error objects are documented here, notice they contain the source of the failure (e.g. src: 'find').

Upvotes: 0

Bluewings
Bluewings

Reputation: 3488

All the JSON store calls ( like add , init etc) are asynchronous. So only you are getting that weird flows when you are checking with Breakpoints.

To get you execution sequence try to move the queryModels(); once the credentials are loaded.

WL.JSONStore.get(collectionName) 
                        .add(credentials)
                        .then(function() {
                            console.log("credentials loaded " + credentials.url);
                            queryModels();
                        })

Upvotes: 1

Related Questions