0
votes

I wrote some backend code for a Parse.com mobile app a couple of years ago, and have just been asked to add a feature. However, I found that after a small tweak the code wouldn't succeed. So, I rolled back to the working copy, downloaded, then deployed that back and it wouldn't work either! I wonder if this is a change in the Parse software?

The code is failing at the save method as all the logs are fine until then. The log for the error case shows 'No message provided'. If I don't use the message attribute it just shows '{}', so I presume it's empty. I have put the promise resolution in the error case to stop the job timing out while I debug. One thing I have never understood is why I have to make two Seed objects and piggy-back off one to save correctly. If I did a.save(null,...) it wouldn't work.

Any help would be fantastic. Thanks!

PS: Apologies for the indenting below - it is correct in my file.

function flush() {
  //Clear the previous records from the class.
  var Seed = Parse.Object.extend("Seeds");
  var _ = require("underscore");
  var arr = [];
  var query = new Parse.Query(Seed);
  return query.find().then(function(oldSeeds) {
    _.each(oldSeeds, function(oldSeed) {
      arr.push(oldSeed.destroy());
    });
    return Parse.Promise.when(arr);
  });
}

Parse.Cloud.job("fetchjson", function(request, status) {

  var url = 'someurl';

  flush().then(function() { Parse.Cloud.httpRequest({url: url}).then(function(httpResponse){
    var Seed = Parse.Object.extend("Seeds");
    var jsonobj = JSON.parse(httpResponse.text);
    var _ = require("underscore");

    var results = [];
    // do NOT iterate arrays with `for... in loops`
    _.each(jsonobj.seeds, function(s) {

        var p = new Parse.Promise();
        results.push(p); // Needs to be done here or when() will execute immediately with no promises.
        var seed = new Seed();
        var a = new Seed(s);
        var image_url = a.get("image")

        //Get the JSON.
        Parse.Cloud.httpRequest({url: image_url}).then(function(response) {
          console.log("Fetching image at URL: " + image_url);
          //Create a new image object and save, passing ref through promise.
          var file = new Parse.File('thumb.jpg', { base64: response.buffer.toString('base64', 0, response.buffer.length) });
          return file.save();
        }).then(function(thumb) {
          console.log("Attaching thumb to object");
          //Set image ref as object attribute.
          a.set("imageFile", thumb);
          console.log("Parsing views into viewsint");
          //Save decimal string as int into another attribute.
          a.set("viewsInt", parseInt(a.get("views")));
      console.log("Parsing description into descriptionarray");
          //Save string as array into another attribute.
      var dar = new Array(1);
      //dar[0] = a.get("description")
          a.set("descriptionarray", [a.get("description")]);
        }, function(error) {
          console.log("Error occurred :(");
        }).then(function(){
          console.log("Saving object");
          //Save the object and resolve the promise so we can stop.
          seed.save(a,{
            success: function(successData){
              console.log(successData);
              p.resolve(successData);
            },
            error: function(error){
              console.log(error.message);
              p.resolve(error);
            }
          });
        });
    });
    // .when waits for all promises to be resolved. This is async baby!
    Parse.Promise.when(results).then(function(data){
      console.log("All objects saved");
      status.success("Updated Succesfully");
    });
  }, function(error) {
    //Oh noes :'(
    console.error('Request failed with response code ' + httpResponse.status);
    status.error("Update Failed");
  });
});
});
2

2 Answers

0
votes

I changed your code a bit and put some comments to explain:

// DEFINE THESE ON THE TOP. NO NEED TO REPEAT.
var _ = require("underscore");
var Seed = Parse.Object.extend("Seeds");

function flush() {
  //Clear the previous records from the class.
  var arr = [];
  var query = new Parse.Query(Seed);
  return query.find().then(function(oldSeeds) {
    _.each(oldSeeds, function(oldSeed) {
      arr.push(oldSeed.destroy());
    });
    return Parse.Promise.when(arr);
  });
}

Parse.Cloud.job("fetchjson", function(request, status) {

  var url = 'someurl';

  flush().then(function() {
    Parse.Cloud.httpRequest({url: url}).then(function(httpResponse){
      var jsonobj = JSON.parse(httpResponse.text);

      var results = [];

      _.each(jsonobj.seeds, function(s) {

        // ONE SEED OBJECT WITH INITIAL SET OF DATA FROM JSON
        var seed = new Seed(s);

        var image_url = seed.get("image")

        // A SERIAL PROMISE FOR EACH SEED
        var promise = Parse.Cloud.httpRequest({url: image_url}).then(function(response) {
          console.log("Fetching image at URL: " + image_url);
          //Create a new image object and save, passing ref through promise.
          var file = new Parse.File('thumb.jpg', { base64: response.buffer.toString('base64', 0, response.buffer.length) });
          return file.save();
        }).then(function(thumb) {
          // SETTING MORE PROPERTIES
          //Set image ref as object attribute.
          console.log("Attaching thumb to object");
          seed.set("imageFile", thumb);

          //Save decimal string as int into another attribute.
          console.log("Parsing views into viewsint");
          seed.set("viewsInt", parseInt(seed.get("views")));

          //Save string as array into another attribute.
          console.log("Parsing description into descriptionarray");
          seed.set("descriptionarray", [seed.get("description")]);

          // SAVING THE OBJECT
          console.log("Saving object");
          return seed.save();
        });

        // PUSH THIS PROMISE TO THE ARRAY TO PERFORM IN PARALLEL
        results.push(promise);
      });

      Parse.Promise.when(results).then(function(data){
        console.log("All objects saved");
        status.success("Updated Succesfully");
      });
    }, function(error) {
      console.error('Request failed with response code ' + httpResponse.status);
      status.error("Update Failed");
    });
});
});
0
votes

Thanks knshn. I had refactored the code a lot since that version (including several of the changes you made), but I had posted the version that was identical to that which was working fine before. Your changes let me see the right error. For some reason doing the simple single object implementation didn't work for me originally, hence the nasty workaround. It works now though.

I have now found the culprit - the Seed class had an attribute called 'id'. With the old version this worked fine, but when I deployed that code now it gave an error 101: 'object not found for update'. This must be because the new Parse code is mixing that up with the internal objectId and getting confused that the id is different to what it expects. I wonder how that could still work with the rollback though. Perhaps the at version was tagged to use the older Parse code.

My fix was to use a different name for the id - 'seed_id'.