Как запрашивать объекты в CloudCode перед сохранением?

Я пытаюсь сравнить новый объект с оригиналом, используя функцию CloudCode beforeSave. Мне нужно сравнить поле, отправленное в update, с существующим значением. Проблема в том, что я не могу правильно получить объект. Когда я запускаю запрос, я всегда получаю значение из отправленного объекта.

ОБНОВЛЕНИЕ: я попробовал другой подход и смог получить старый регистр (тот, который уже сохранен в разборе). Но новый, отправленный в запросе, был заменен старым. КАКИЕ?! Другая проблема заключается в том, что даже несмотря на то, что код отправил response.success(), обновление не было сохранено. Я полагаю, что здесь не хватает чего-то довольно очевидного. Или я столкнулся с ошибкой или что-то в этом роде...

НОВЫЙ ПОДХОД

Parse.Cloud.beforeSave('Tasks', function(request, response) {
  if ( !request.object.isNew() )
  {
    var Task = Parse.Object.extend("Tasks");
    var newTask = request.object;
    var oldTask = new Task();
    oldTask.set("objectId", request.object.id);
    oldTask.fetch()
        .then( function( oldTask )
    {
        console.log(">>>>>> Old Task: " + oldTask.get("name") + " version: " + oldTask.get("version"));
        console.log("<<<<<< New Task: " + newTask.get("name") + " version: " + newTask.get("version"));
        response.success();
    }, function( error ) {
            response.error( error.message );
        }
    );
    }
});

ОБЪЕКТ ОТПРАВЛЕН {"name":"LLL", "version":333}

ЖУРНАЛ

 I2015-10-02T22:04:07.778Z]v175 before_save triggered for Tasks for user tAQf1nCWuz:
 Input: {"original":{"createdAt":"2015-10-02T17:47:34.143Z","name":"GGG","objectId":"VlJdk34b2A","updatedAt":"2015-10-02T21:57:37.765Z","version":111},"update":{"name":"LLL","version":333}}
 Result: Update changed to {}
 I2015-10-02T22:04:07.969Z]>>>>>> Old Task: GGG version: 111
 I2015-10-02T22:04:07.970Z]<<<<<< New Task: GGG version: 111

ПРИМЕЧАНИЕ. Я тестирую вход через cURL и в консоли синтаксического анализа.

CloudCode перед сохранением

Parse.Cloud.beforeSave("Tasks", function( request, response) {
  var query = new Parse.Query("Tasks");
  query.get(request.object.id)
    .then(function (oldObj) {
        console.log("-------- OLD Task: " + oldObj.get("name") + " v: " + oldObj.get("version"));
        console.log("-------- NEW Task: " + request.object.get("name") + " v: " + request.object.get("version"));
    }).then(function () {
        response.success();
    }, function ( error) {
        response.error(error.message);
    }
  );
});

запрос cURL

curl -X PUT \
-H "Content-Type: application/json" \
-H "X-Parse-Application-Id: xxxxx" \
-H "X-Parse-REST-API-Key: xxxxx" \
-H "X-Parse-Session-Token: xxxx" \
-d "{\"name\":\"NEW_VALUE\", \"version\":9999}" \
https://api.parse.com/1/classes/Tasks/VlJdk34b2A

Ответ JSON

"updatedAt": "2015-10-02T19:45:47.104Z"

ЖУРНАЛ В журнале печатается исходное и новое значение, но я также не знаю, как получить к нему доступ.

I2015-10-02T19:57:08.603Z]v160 before_save triggered for Tasks for user tAQf1nCWuz:
Input: {"original":{"createdAt":"2015-10-02T17:47:34.143Z","name":"OLD_VALUE","objectId":"VlJdk34b2A","updatedAt":"2015-10-02T19:45:47.104Z","version":0},"update":{"name":"NEW_VALUE","version":9999}}
Result: Update changed to {"name":"NEW_VALUE","version":9999}
I2015-10-02T19:57:08.901Z]-------- OLD Task: NEW_VALUE v: 9999
I2015-10-02T19:57:08.902Z]-------- NEW Task: NEW_VALUE v: 9999

person Tin Megali    schedule 02.10.2015    source источник
comment
Я не уверен, что ты сможешь это сделать. С какой целью вы это делаете? Возможно, мы найдем другое решение.   -  person Jake T.    schedule 02.10.2015
comment
Мне нужно проверить версию реестра и сравнить с версией, отправленной в запросе.   -  person Tin Megali    schedule 02.10.2015
comment
Это очень странно, я действительно не понимаю, почему ваша версия с надписью «НОВЫЙ ПОДХОД» не работает... должна. Я вижу вашу заметку о Parse.Object.disableSingeInstance, но я впервые об этом слышу. Похоже, что это очень неэффективно, так как вам нужно дважды получить объект. Я связывался с Гектором через Twitter или FB и спрашивал об этом.   -  person MobileVet    schedule 06.10.2015
comment
@MobileVet Я полностью с тобой согласен. В производственном сценарии мое решение было бы нецелесообразно. На самом деле, эта проблема настолько сумасшедшая, что я подумываю полностью отказаться от Parse. Если этот парень, Гектор, из Parse.com, не могли бы вы дать мне его контакты? Может быть, он сможет спасти меня от этого ада.   -  person Tin Megali    schedule 06.10.2015
comment
@MobileVet У меня уже есть его контакты. Спасибо за совет. сейчас попробую с ним связаться   -  person Tin Megali    schedule 06.10.2015
comment
Дайте мне знать, что вы слышите. Извините, что медленно отвечал, я был вдали от «битов» на долгие выходные.   -  person MobileVet    schedule 07.10.2015


Ответы (5)


После множества проб и ошибок я смог понять, что происходит.

Выяснилось, что Parse объединяет все объекты с одинаковым классом и идентификатором в один экземпляр. По этой причине у меня всегда был либо объект, зарегистрированный в БД, либо объект, отправленный пользователем. Честно говоря, я не понимаю смысла такого поведения, но тем не менее...

Parse javascript sdk предлагает метод с именем Parse.Object.disableSingeInstance ссылка, отключает эту "функцию". Но после вызова метода все уже определенные объекты становятся неопределенными. Это включает в себя отправленный объект. Ведьма означает, что вы не можете ни сохранить отправленный объект для дальнейшего использования.

Единственным вариантом было сохранить ключ и значения отправленного объекта и воссоздать его позже. Итак, мне нужно было захватить запрос перед вызовом disableSingleInstance, преобразовать его в JSON, затем отключить один экземпляр, получить объект, сохраненный в БД, и воссоздать отправленный объект, используя сохраненный JSON.

Это не красиво и определенно не самый эффективный код, но я не мог найти другого способа. Если у кого-то есть другой подход, обязательно скажите мне.

Parse.Cloud.beforeSave('Tasks', function(request, response) {
  if ( !request.object.isNew() ) {
    var id = request.object.id;
    var jsonReq;
    var Task = Parse.Object.extend("Tasks");
    var newTask = new Task;
    var oldTask = new Task;

    // getting new Obj
    var queryNewTask = new Parse.Query(Task);
    queryNewTask.get(id)
        .then(function (result) {
            newTask = result;

            // Saving values as a JSON to later reference
            jsonReq = result.toJSON();

            // Disable the merge of obj w/same class and id
            // It will also undefine all Parse objects,
            // including the one sent in the request
            Parse.Object.disableSingleInstance();

            // getting object saved in DB
            oldTask.set("objectId", id);
            return oldTask.fetch();
        }).then(function (result) {
            oldTask = result;

            // Recreating new Task sent
            for ( key in jsonReq ) {
                newTask.set( key, jsonReq[key]);
            }

            // Do your job here
        }, function (error) {
            response.error( error.message );
        }
    );
  }
});
person Tin Megali    schedule 06.10.2015

На вашем месте я бы передал старое значение в качестве параметра облачной функции, чтобы вы могли получить к нему доступ в request.params.(имя параметра). Я не верю, что есть другой способ получить старое значение. В старом вопросе SO говорилось, что вы можете использовать .get(), но вы утверждаете, что это не работает. Если только у вас уже не было 9999 в версии...

edit - я думаю, что beforeSave не вызывается как обычная функция ... поэтому, возможно, создайте функцию «обновить версию», которая проходит в текущей задаче и версии, которую вы пытаетесь обновить?

person Jake T.    schedule 02.10.2015
comment
Я не могу этого сделать. Когда я говорю СТАРЫЙ, я имею в виду регистр, сохраненный в БД. Мне нужно сравнить новый регистр - person Tin Megali; 02.10.2015
comment
Parse не позволяет передавать параметры хуку beforeSave. Источник: parse.com/questions/how-to- параметры запроса доступа перед сохранением - person Russell; 02.10.2015
comment
@Рассел, посмотри, что я сделал. TinMegali, вам нужно будет передать текущее значение и новое значение, которое вы пытаетесь изменить, в отдельную функцию, выполнить сравнение, а затем сохранить, если они соответствуют вашим требованиям. - person Jake T.; 05.10.2015

Вместо того, чтобы выполнять запрос, вы можете увидеть измененные атрибуты, проверив, какие ключи являются грязными, то есть они были изменены, но еще не сохранены.

JS SDK включает dirtyKeys(), который возвращает измененные ключи. Попробуйте это.

var attributes = request.object.attributes; 
var changedAttributes = new Array(); 

for(var attribute in attributes) {

    if(object.dirty(attribute)) { 
        changedAttributes.push(attribute);
        // object.get(attribute) is changed and the key is pushed to the array
    }
}

Для пояснения, чтобы получить исходное значение атрибута, вам нужно будет вызвать get() для загрузки этих предварительно сохраненных значений. Следует отметить, что это будет считаться еще одним запросом API.

person Russell    schedule 02.10.2015
comment
Согласно блогу разбора, dirtyKey полезно сравнивать с полем, которое было изменено, верно? Но мне нужны значения определенного поля. Мне нужно сравнить значение зарегистрированного поля с тем же значением поля в запросе. Есть ли способ сделать это с помощью dirtyKey? - person Tin Megali; 02.10.2015
comment
Просто обновил свой пост, чтобы уточнить. Вы правы в том, что dirtyKey можно использовать только для того, чтобы сообщить, какие атрибуты были изменены, но не значения ключей заранее. Их нужно будет получить с помощью другого запроса. Parse не предоставляет previousAttributes() или previous("columnName") в JS SDK. - person Russell; 02.10.2015
comment
Да, но это моя проблема. Когда я пытаюсь получить значение, хранящееся в БД внутри функции beforeSave, я получаю значение, отправленное пользователем, а не то, которое было сохранено при разборе. Предположим, что класс X имеет сохраненное значение 100, а пользователь отправляет 900. Когда я пытаюсь получить класс X, используя objId, отправленный пользователем, я получаю 900. Точно такое же значение, которое было отправлено пользователем? Проверьте это мой запрос в вопросе. - person Tin Megali; 02.10.2015
comment
Я считаю, что проблема в том, что запрос выполняется асинхронно. К моменту выполнения обещания запроса измененный объект уже сохранен. Переместите response.success() под двумя операторами регистрации. Вызывая response.success(), вы сигнализируете, что сохранение объекта может продолжаться. - person Russell; 03.10.2015
comment
Насколько я понимаю, ответ был вызван в другом обещании, следовательно, срабатывает только после печати консоли. Верно? Я новичок в обещаниях, но мне кажется, что это правильно. Взгляните на код и поправьте меня, если я ошибаюсь. Ткс =) - person Tin Megali; 03.10.2015
comment
.then(function (oldObj) { console.log ... }).then(function () { response.success(); - person Tin Megali; 03.10.2015
comment
Если вы сомневаетесь, добавьте больше инструкций console.log, чтобы вы могли отслеживать выполнение. Вы правы в том, что цепочка последовательных обещаний должна выполняться после печати консоли, но я не вижу причин, по которым у вас вообще есть цепочка. Вы можете сделать все в рамках того же обещания, что и есть. - person Russell; 03.10.2015
comment
Кроме того, это также может помочь: stackoverflow.com/questions/25082262/ - person Russell; 03.10.2015
comment
Ну, я создал цепочку, потому что думал, что проблема была в асинхронном вызове, как вы и сказали. Но, Не помогло! ржу не могу - person Tin Megali; 03.10.2015
comment
Обещания по-прежнему выполняются асинхронно, это просто еще одно соглашение JS для обработки обратных вызовов. - person Russell; 03.10.2015

Эй, это отлично сработало для меня:

var dirtyKeys = request.object.dirtyKeys();
var query = new Parse.Query("Question");
var clonedData = null;
        query.equalTo("objectId", request.object.id);
        query.find().then(function(data){
            var clonedPatch = request.object.toJSON();
            clonedData = data[0];
            clonedData = clonedData.toJSON();
            console.log("this is the data : ", clonedData, clonedPatch, dirtyKeys);
            response.success();
        }).then(null, function(err){
            console.log("the error is : ", err);
        });
person daksh_019    schedule 17.09.2016

Для тех, кто зайдет в эту ветку в 2021 году: если у вас есть данные сервера, загруженные в клиентский SDK перед сохранением, вы можете решить эту проблему, передав данные сервера из клиентского SDK в context вариант функции save(), а затем использовать его в beforeSave afterSave облачных функциях.

// eg JS client sdk
const options  = { 
  context: {
    before: doc._getServerData() // object data, as loaded
  } 
}
doc.save(null, options)
// #beforeSave cloud fn
Parse.Cloud.beforeSave(className, async (request) => {
  const { before } = request.context
  // ... do something with before ... 
})

Предупреждение: это не помогло бы вам, если бы у вас не было атрибутов, загруженных в функцию _getServerData() в клиенте

Второе предостережение: синтаксический анализ не будет обрабатывать (не) сериализацию для вас в вашей облачной функции, например:

{
  before: { // < posted as context
    status: {
      is: 'atRisk',
      comment: 'Its all good now!',
      at: '2021-04-09T15:39:04.907Z', // string
      by: [Object] // pojo
    }
  },
  after: {
    status: { // < posted as doc's save data
      is: 'atRisk',
      comment: 'Its all good now!',
      at: 2021-04-09T15:39:04.907Z, // instanceOf Date
      by: [ParseUser] // instanceOf ParseUser
    }
  }
}
person Tremendus Apps    schedule 14.04.2021