3

The ultimate goal is to detect changes between an existing Parse object and the incoming update using the beforeSave function in Cloud Code.

From the Cloud Code log available through parse.com, one can see the input to beforeSave contains a field called original and another one called update.

Cloud Code log:

Input: {"original": { ... }, "update":{...}

I wonder if, and how, we can access the original field in order to detect changing fields before saving.

Note that I've already tried several approaches for solving this without success:

  • using (object).changedAttributes()
  • using (object).previousAttributes()
  • fetching the existing object, before updating it with the new data

Note on request.object.changedAttributes(): returns false when using in beforeSave and afterSave -- see below for more details:

Log for before_save -- summarised for readability:

Input: { original: {units: '10'}, update: {units: '11'} }
Result: Update changed to { units: '11' }
[timestamp] false <--- console.log(request.object.changedAttributes())

Log for corresponding after_save:

[timestamp] false <--- console.log(request.object.changedAttributes())
danh
  • 62,181
  • 10
  • 95
  • 136
marco alves
  • 1,707
  • 2
  • 18
  • 28
  • 1
    `object.changedAttributes()` is the correct approach. Please post your attempt related to that, and we'll make it work. – danh Jun 28 '15 at 23:59
  • `request.object.changedAttributes()` returns `false` – marco alves Jun 29 '15 at 08:30
  • If you can produce a demonstration of that (changing an object, then finding `changedAttributes() === false`), then you'll have found a parse.com bug. I suspect, though, that you're mistaken. Please prove me wrong by posting a failure others can reproduce. – danh Jun 29 '15 at 14:25
  • Added details on the case I'm talking about. Note that I'm talking about Cloud Code, and not using the JS SDK on the client side. – marco alves Jun 30 '15 at 09:21
  • Please provide a minimal reproducible example (MRE). Say, a class with one attribute, an afterSave hook for that class that logs changedAttributes and client code that changes the one attribute and does a save. Prove that the client makes the change, prove that the afterSave doesn't see changedAttributes. If you can prove it, you'll have found a bug in parse, but almost certainly, you'll discover your original mistake while building the MRE. – danh Jun 30 '15 at 15:28
  • PS - I ask SO questioners for an MRE every so often, and 9.5 times out of 10, its the last activity seen on the question. I figure that this is because (a) good outcome: doing the MRE will often lead the questioner to the answer, or (b) bad outcome, the questioner is unwilling to put in the MRE effort, and just drops it. – danh Jun 30 '15 at 15:32
  • I don't see the point of including client code since the parse.com cloud code log recognizes that an update has occurred. – marco alves Jun 30 '15 at 15:32
  • client code, because maybe you're mistaken to think that you actually changed the object. and server code, please. code+logs is the only way to see what's happening. – danh Jun 30 '15 at 15:33
  • If the object did not change, parse.com should not recognise that update has occurred as an output of Cloud Code – marco alves Jun 30 '15 at 15:36
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/81996/discussion-between-danh-and-marco-alves). – danh Jun 30 '15 at 15:42
  • 1
    My apologies. I assumed you had a bug and just blamed parse. I decided, arrogantly, that if you were too lazy to build an MRE, I would do it and show you the error in your ways. Imagine my surprise to find the same issue! Sorry again. As penance, I built a work-around and posted it below. – danh Jun 30 '15 at 20:39
  • @danh Thank you for your willingness to going the extra mile and for the work around. – marco alves Jun 30 '15 at 21:51

1 Answers1

2

There is a problem with changedAttributes(). It seems to answer false all the time -- or at least in beforeSave, where it would reasonably be needed. (See here, as well as other similar posts)

Here's a general purpose work-around to do what changedAttributes ought to do.

// use underscore for _.map() since its great to have underscore anyway
// or use JS map if you prefer...

var _ = require('underscore');

function changesOn(object, klass) {
    var query = new Parse.Query(klass);
    return query.get(object.id).then(function(savedObject) {
        return _.map(object.dirtyKeys(), function(key) {
            return { oldValue: savedObject.get(key), newValue: object.get(key) }
        });
    });
}

// my mre beforeSave looks like this
Parse.Cloud.beforeSave("Dummy", function(request, response) {
    var object = request.object;
    var changedAttributes = object.changedAttributes();
    console.log("changed attributes = " + JSON.stringify(changedAttributes));  // null indeed!

    changesOn(object, "Dummy").then(function(changes) {
        console.log("DIY changed attributes = " + JSON.stringify(changes));
        response.success();
    }, function(error) {
        response.error(error);
    });
});

When I change someAttribute (a number column on a Dummy instance) from 32 to 1222 via client code or data browser, the log shows this:

I2015-06-30T20:22:39.886Z]changed attributes = false

I2015-06-30T20:22:39.988Z]DIY changed attributes = [{"oldValue":32,"newValue":1222}]

danh
  • 62,181
  • 10
  • 95
  • 136