Start a new topic

Request in postFetch is always empty

I have a collection called Profile, and I want to populate its emailAddresses column with the results of a postFetch query (that query looks for emails in the Emails collection). However, I can't get the entire operation to even start because the request body is always empty, giving me a null profile var. 


Am I wrongly assuming the request object is filled at all times, and has the Profile collection row in its body property?

function onPostFetch(request, response, modules) {
 var emailCollection = modules.collectionAccess.collection("emails");
var profileCollection = modules.collectionAccess.collection("profile");
var info = modules.logger.info;
var profile = request.body;
if (profile) {
var creator = profile._acl.creator;
modules.async.each(response.body, function(aProfile, callback) {
emailCollection.find({"_acl.creator" : creator}, function(err,emails){
if (!err) {
info("Emails: " + emails);
aProfile.emailAddresses = emails;
info(aProfile);
}
callback();
});
}, function(err){
response.continue();
});
} else {
response.continue();
}
}

 


Hi Andre,


The request object represents the incoming request from the app to the Kinvey backend. The response object represents the outgoing response from the Kinvey backend to the app for that incoming request.


The request body might or might not be empty depending on the request format. And response.body will be an array of rows with zero or more entries.


Following lines seem to be the culprit

  

var profile = request.body;
var creator = profile._acl.creator;

  

If the request returns only one row, above line should look as follows:

   

var profile = response.body;
var creator = profile[0]._acl.creator;

   

I hope this is helpful.



Regards,

Wani


1 person likes this

Getting runtime errors for the code now.

function onPostFetch(request, response, modules) {
	var emailCollection = modules.collectionAccess.collection("emails");
  var profileCollection = modules.collectionAccess.collection("profile");
  var info = modules.logger.info;
  
  var profile = request.body[0];
  
  if (profile) {
    var creator = profile._acl.creator;
  
    modules.async.each(response.body, function(aProfile, callback) {
        
      emailCollection.find({"_acl.creator" : creator}, function(err,emails){
        if (!err) {
          info("Emails: " + emails);
            aProfile.emailAddresses = emails;
            info(aProfile);
          }
          
          callback();
      });  
    
    }, function(err){
        response.continue();
    });
      
  } else { 
    response.continue();
  }
}

 What I'm trying to do here is, on postFetch, fetch all e-mail addresses from Emails collection matching the same _acl.creator as the Profile. Is that even possible? (i.e. "filling" Profile fields with information from other collections on every fetch operation - I'm using this approach to ensure better scaling possibilities and also up-to-date data)

Hi,


Can you share the runtime errors that you are getting?


To reiterate the use case, when you fetch profiles from "profile" collection, in postFetch BL, you want to get entries from "email" collection respective to that profile and append it to the response JSON. Is that right?



Regards,

Wani

Hello Wani,


I'm no longer getting any runtime errors, just the following 200 response:

200 SUCCESS -- [ { "_id": "546b864cb76664a0030084e7", "firstName": "TEST", "middleName": "TEST", "lastName": "TEST", "nickname": "TEST", "company": "TEST", "department": "TEST", "jobTitle": "TEST", "prefix": "TEST", "suffix": "TEST", "notes": "TEST", "addresses": [ "Test 1", "Test 2" ], "emailAddresses": [ "Bogus" ], "keycode": "kid_-yEfkj_t7", "_acl": { "creator": "kid_-yEfkj_t7" }, "_kmd": { "lmt": "2015-12-06T20:40:35.246Z", "ect": "2014-11-18T17:47:56.300Z" } }, { "_id": "563fa230891ccddd53049832", "_acl": { "creator": "kid_-yEfkj_t7" }, "firstName": "Madonna", "lastName": "Ciccone", "nickname": "Madge", "jobTitle": "Pop singer", "_kmd": { "lmt": "2015-11-08T19:27:44.960Z", "ect": "2015-11-08T19:27:44.960Z" } }, { "_id": "563fa93cd2c9f27544027711", "_acl": { "creator": "kid_-yEfkj_t7" }, "firstName": "Finalmente", "lastName": "Porra", "middleName": "Foi", "_kmd": { "lmt": "2015-11-08T19:58:34.300Z", "ect": "2015-11-08T19:57:48.870Z" } }, { "_id": "56649dc7af5de3b318004687", "_acl": { "creator": "kid_-yEfkj_t7" }, "_kmd": { "lmt": "2015-12-06T20:42:47.575Z", "ect": "2015-12-06T20:42:47.575Z" } } ]

 My code right now, as it stands, never fills "emailAddresses" with results from Email collection:

function onPostFetch(request, response, modules) {
	var emailCollection = modules.collectionAccess.collection("emails");
  var profileCollection = modules.collectionAccess.collection("profile");
  var info = modules.logger.info;
  
  var profile = request.body[0];
  
  if (profile) {
    var creator = profile._acl.creator;
  
    modules.async.each(response.body, function(aProfile, callback) {
        
      emailCollection.find({"_acl.creator" : creator}, function(err,emails){
        if (!err) {
          info("Emails: " + emails);
            aProfile.emailAddresses = emails;
            info(aProfile);
          }
          
          callback();
      });  
    
    }, function(err){
        response.continue();
    });
      
  } else { 
    response.continue();
  }
}

 Is that code correct? Should I be seeing the data from the Email collection "merged" with the Profile fetch results right on the test console? Or perhaps in the data browser? Or do hooks only work when making "real" requests using the API? I'm not very familiar with Javascript, so I'm trying to infer behaviors from my native mobile experience.

Hi,


There are a couple of issues with this code:

  • The query uses creator of first row instead of using creator of each row.
  • Async each function does not help you save modified profiles back to response body.

I have rewritten the code for you. Please try the code below and let me know if it works for you.

 

function onPostFetch(request, response, modules) {
	var emailCollection = modules.collectionAccess.collection("emails");
	var profileCollection = modules.collectionAccess.collection("profile");
	var info = modules.logger.info;
	var async = modules.async;

	if (response.body.length > 0){
		async.map(response.body,
			function(aProfile, callback) {
				emailCollection.find({"_acl.creator" : aProfile._acl.creator}, function(err,emails){
					if (err) {
						callback(err);
					} else {
						// info("Emails: " + emails);
						aProfile.emailAddresses = emails;
						// info(aProfile);
						callback(null,aProfile);
					}
				});
			, function(err, results){
			    if (err){
			    	info(err);
			    	response.complete(500);
			    } else {
			    	response.body = results;
			    	response.complete(200);
			    }
		});
	}
}

 

To answer your other question, the hooks work the same way with editor or with a request of any other type - from the mobile app or from Kinvey API console.


Also, I would suggest following resources that will help with Business Logic in future:

  1. Kinvey BL guide
  2. Kinvey BL reference
  3. NPM async documentation


Regards,

Wani


1 person likes this

Thank you very much for the Business Logic resources, the examples in there are actually quite useful.


I tried your corrections on my code had a timeout error. Is this kind of approach not a good idea with Kinvey (i.e. merging tables via hooks)?

function onPostFetch(request, response, modules) {
    var emailCollection = modules.collectionAccess.collection("emails");
    var profileCollection = modules.collectionAccess.collection("profile");
    var info = modules.logger.info;
    var async = modules.async;
 
    if (response.body.length > 0) {
        async.map(response.body, function(aProfile, callback) {
                                    emailCollection.find({"_acl.creator" : aProfile._acl.creator}, function(err,emails) {
                                        if (err) {
                                            callback(err);
                                        } else {
                                            // info("Emails: " + emails);
                                            aProfile.emailAddresses = emails;
                                            // info(aProfile);
                                            callback(null,aProfile);
                                        }
                                    })
                                , function(err, results) {
                                    if (err){
                                        info(err);
                                        response.complete(500);
                                    } else {
                                        response.body = results;
                                        response.complete(200);
                                    }
                                }
        });
    }
}

  

500 INTERNAL SERVER ERROR -- { "error": "BLTimeoutError", "description": "The Business Logic script did not complete. Please contact support", "debug": "The script was terminated due to timing constraints: took more than 2000ms to complete. Did you forget to call response.complete() or response.continue()?" } REQUEST 2015-12-19T20:37:32-02:00

 

Hi,


For your current pricing plan, the BL timeout limit is 2 seconds.


The important thing here is the number of rows you are trying to process. Can you give me rough estimates for the following:

  1. The total number of profiles (current and expected)
  2. The total number of emails (current and expected)
  3. Average number of emails per profile
  4. Average number of profiles fetched in one GET call

If these numbers are too large, BL will take too much time to complete. In that case, you should consider alternate approaches.

Regards,
Wani

Right now I only have 3 Profiles, and only one of them fetches 2 Emails. So I guess Kinvey takes more than 2 seconds for a one-record merge operation. Is that expected?


Regards,

André

Hi, your code is timing out because it contains an error: the last callback (the one with the function(err, results) signature) is contained inside of the iterator function you are passing to async. Please check your curly brackets, and try again once you've fixed the issue.
Login or Signup to post a comment