Start a new topic
Answered

Custom End-Point objects to Kinvey Entity

I have created a custom endpoint and it is supposed to return 10 kinvey entities just like KCSLinkedAppStore queries. However I get raw JSON objects that are not actual Kinvey Entities.

Please help as I think it's utterly important to be able to receive Kinvey entities from calling an endpoint.


  activityCollection.find({"fromUser._id":forUserId, "type": "follow", "content" : "accepted"}, function (err, docs) {
    if (err) {
      logger.error('Query failed: '+ err);
      response.error(err);
    } else {
            response.body = docs; // these should be Kinvey Entities, but instead it's a raw JSON
            response.complete(200);
        }
      });

 


Best Answer
Grisha,

I have confirmed this with the engineering team.

Custom Endpoints are not attached to an Entity class, for that reason we don’t provide a built-in solution in this case. You can definitely create an init method (as discussed earlier) or a custom parser for this scenario, or use an object mapper library of your choice.

You can also explore the 3.x version of the SDK where it would be a little easier to do the conversion since there is a better object mapping support built into the library.
http://devcenter.kinvey.com/ios-v3.0/downloads#

Thanks,
Pranav
Kinvey Support

 


Ignore previous code snippet,
This is the code I am using :

 

activityCollection.find({"fromUser._id":forUserId, "type": "follow", "content" : "accepted"}, function (err, docs) {
  if (err) {
    logger.error('Query failed: '+ err);
    response.error(err);
  } else {
          var activities = []
          for (doc in docs) {
                activities.push(modules.kinvey.entity(doc));
          }
          response.body = activities; // these should be Kinvey Entities, but instead it's a raw JSON
          response.complete(200);
      }
    });

 

Grisha,


If you add the following logger statements at relevant places in your code, can you let me know their outputs?


  • logger.info(modules.kinvey.entity(doc));
  • logger.info(activities);


How it differs from the expected output?


Thanks,

Pranav

Kinvey Support

 

            var activities = [];
            for (var i = 0; i < docs.length; i++) {
                activities.push(modules.kinvey.entity(docs[i]));
                lg.info(modules.kinvey.entity(docs[i]));
            }
            lg.info("____");
            lg.info(activities);
            response.body = activities;
            response.complete(200);

 This outputs following:


 
    
      "_id":"56b336e67dbaabeb04014204",
      "author": 
         "_type":"KinveyRef",
         "_id":"55b3cf59eaf8fd4a0c024038",
         "_collection":"user"
      },
      "details":"Dear All Students, \nToday we will be showing an exhibition of work from our WSA 2nd year Marketing students! The event will comprise of work highlighting gender neutrality and androgynous styles in Fashion!\n\nPlease come along and check it out!\n",
      "access":"public",
      "authorId":"55b3cf59eaf8fd4a0c024038",
      "pictureId":"cb7bdf00-2097-47ab-9ae9-6bfb7102bda5",
      "date":"ISODate(\"2016-02-04T13:00:00.000Z\")",
      "_acl": 
         "creator":"55b3cf59eaf8fd4a0c024038"
      },
      "createdAt":"ISODate(\"2016-03-15T01:08:49.706Z\")",
      "_kmd": 
         "lmt":"2016-07-02T00:58:20.715Z",
         "ect":"2016-02-04T11:32:54.417Z"
      },
      "name":"WSA ART EXHIBITION"
   },
    
      "_id":"56b337ca7540b68d6d00c59e",
      "author": 
         "_type":"KinveyRef",
         "_id":"55b3cf59eaf8fd4a0c024038",
         "_collection":"user"
      },
      "details":"Welcome all. This week we are holding an event hosted by RAG our charity segment of SUSU! We are currently taking donations for a range of good causes so start February with some good KARMA and donate generously! \n\nLove,\n\nAnjit (Student Union Vice President)",
      "access":"public",
      "authorId":"55b3cf59eaf8fd4a0c024038",
      "pictureId":"e39ebe5c-f51a-4083-95c4-49665ace2594",
      "date":"ISODate(\"2016-02-04T11:33:05.324Z\")",
      "_acl": 
         "creator":"55b3cf59eaf8fd4a0c024038"
      },
      "createdAt":"ISODate(\"2016-03-15T00:49:31.657Z\")",
      "_kmd": 
         "lmt":"2016-07-02T00:58:20.717Z",
         "ect":"2016-02-04T11:36:42.340Z"
      },
      "name":"The BIG give! "
   }
]

"_____"


 
   "_id":"56b337ca7540b68d6d00c59e",
   "author": 
      "_type":"KinveyRef",
      "_id":"55b3cf59eaf8fd4a0c024038",
      "_collection":"user"
   },
   "details":"Welcome all. This week we are holding an event hosted by RAG our charity segment of SUSU! We are currently taking donations for a range of good causes so start February with some good KARMA and donate generously! \n\nLove,\n\nAnjit (Student Union Vice President)",
   "access":"public",
   "authorId":"55b3cf59eaf8fd4a0c024038",
   "pictureId":"e39ebe5c-f51a-4083-95c4-49665ace2594",
   "date":"ISODate(\"2016-02-04T11:33:05.324Z\")",
   "_acl":

Grisha,

In your custom endpoint you will need to handle that case. You need to query the “user” collection for that author given the _id and then insert the resulting user information in “author” field in the response body.
It won’t happen by default, you will need to code it in the custom endpoint.

Thanks,
Pranav
Kinvey Support

 

Dear Pranav,


Thank you, but this is not my problem. My problem is that when I do following in my iOS code:

 

let store = KCSLinkedAppdataStore.storeWithOptions([
            KCSStoreKeyCollectionName : "Events",
            KCSStoreKeyCollectionTemplateClass : Event.self
            ])

store.queryWithQuery(query, withCompletionBlock: {
        (objects:[AnyObject]!, error:NSError!) -> Void in
            if (error == nil){
                var temp:[Event] = []
                for object in objects {
                    print(object)
                    temp.append(object as! Event)
                }
                completionBlock(events: temp, error: nil)
            }else{
                completionBlock(events: nil, error: error)
            }
 }, withProgressBlock: nil)

It works normal.

But when I try the same with the endpoint:

 

KCSCustomEndpoints.callEndpoint(
            "get_followed_content",
            params: nil,
            completionBlock: {
                (objects: AnyObject!, error: NSError!) -> Void in
                if error == nil {
                    var temp:[Event] = []
                    for object in objects  {
                        temp.append(object as! Event)
                    }
                    
                    completionHandler(downloadedEventsArray: temp, error: nil)

                } else {
                    print(error)
                    completionHandler(downloadedEventsArray: [], error: error)
                }
            }
        )

it crashes.

Please have a look at this as it's a major obstacle for my app. 

And it crashes always on this line

temp.append(object as! Event)

It fails to cast downloaded object into the KCSPersistable object.

Please help, I really need to get objects from calling endpoint the same way I do from queryWithQuery.

Grisha,

Can you send the definition of your Event class?

Do you see any difference in the outputs (structure of the json) (when you run them from the Kinvey Console)?

Query 1) Output when querying the collection
Query 2) Output when executing the custom endpoint

What is the Kid of your app and version of Kinvey SDK you are using?

Thanks,
Pranav

Kinvey Support

My definition of Event class:

class Event: NSObject  {
    var entityId: NSString? //Kinvey entity _id
    
    var author: KCSUser!
    var authorId: NSString?
    var name: NSString?
    var details: NSString?
    var pictureId: NSString?
    var date: NSDate?
    var location: NSString?
    var access:NSString?
    var createdAt: NSDate?

    var hidden:NSString?
    var sharedID:NSString?
    
    var metadata: KCSMetadata? //Kinvey metadata, optional
    internal override func hostToKinveyPropertyMapping() -> [NSObject : AnyObject]! {
        return [
            "entityId" : KCSEntityKeyId, //the required _id field
            "author" : "author",
            "name" : "name",
            "details" : "details",
            "pictureId" : "pictureId",
            "authorId" : "authorId",
            "date" : "date",
            "location" : "location",
            "access" : "access",
            "hidden" : "hidden",
            "createdAt": "createdAt",
            "sharedID" : "sharedID",
            "metadata" : KCSEntityKeyMetadata //optional _metadata field
        ]
    }
    
    internal class override func kinveyPropertyToCollectionMapping() -> [NSObject : AnyObject]! {
        return [
            "author" : KCSUserCollectionName,
        ]
    }
}

  

The definition of endpoint is:

 

function onRequest(request, response, modules) {
  
  var lg = modules.logger;
  
  // Get id of user who sent the request
  var requestContext = modules.requestContext;
  var forUserId = requestContext.getAuthenticatedUserId();
  
  // modules.logger.info(forUserId);
  var activityCollection = modules.collectionAccess.collection('Activity');
  var eventsCollection = modules.collectionAccess.collection('Events');
  var usersCollection = modules.collectionAccess.collection('user');

  
  // Now finding all the users that he is following
  activityCollection.find({"fromUser._id":forUserId, "type": "follow", "content" : "accepted"}, function (err, docs) {
    if (err) {
      lg.error('Query failed: '+ err);
      response.error(err);
    } else {
      if (docs.length === 0) {
          response.body = docs;
          response.complete(200);
          return;
      }
      
      var followingUserIds = [];
      for (var i = 0; i < docs.length; i++) {
          followingUserIds.push(docs[i].toUser._id);
      }
      
      // by this moment we should already have following ids
      // let's fetch events
      eventsCollection.find({ "author._id" : {$in: followingUserIds}}, function (err, docs) {
        if (err) {
          logger.error('Query failed: '+ err);
          response.error(err);
        } else {
          
            // Store all event Ids !
            if (docs.length === 0) {
              response.body = docs;
              response.complete(200);
              return;
            }
            
            
            var events = [];
          
          	// for async loading
            var authorsLoaded = 0;
            var authorsToLoad = docs.length;
          
            for (var i = 0; i < docs.length; i++) {
                var event = modules.kinvey.entity(docs[i]);
                usersCollection.find(usersCollection.objectID(event.author._id), function (err, docs) {
                  if (err) {
                    lg.error('Query failed: '+ err);
                    response.error(err);
                  } else {
                    if (docs.length > 0) {
                      authorsLoaded += 1;
                      var ev = {}
                      event.author = docs[0];
                      events.push(event);
                      if (authorsLoaded == authorsToLoad) {
                          lg.info("All Loaded");
                          response.body = events;
                          response.complete(200);
                      }
                    } else {
                      lg.info("no author found");
                      return;
                    }

                  }
                });
            }
        }
      });
    }
  }); 
}

 Endpoint works perfectly (returns correct JSON) , you don't have to understand the code but basically it finds users that are followed by the request user and loads their events.


Now the result from calling the endpoint is:

  

{  
      "_id":"56b337ca7540b68d6d00c59e",
      "author":{  
         "_id":"55b3cf59eaf8fd4a0c024038",
         "access":"public",
         "pictureId":"9963f249-adf5-4fd2-8c04-b0b2841166f6",
         "_messaging":{  
            "pushTokens":[  
               {  
                  "token":"d66f9d002ff0f8b630408fa0d411aa3eb5d9ef946b9930cea1986bedf5a09d97",
                  "platform":"ios",
                  "arn":"arn:aws:sns:us-east-1:853461911189:endpoint/APNS_SANDBOX/kid_W19oFN4H1l_development/a4383c07-565e-34de-bc5f-1d7891d5b1b9"
               },
               {  
                  "token":"cb6ea190e8b8829cacf24f063785c082859130f3910e540192d138ed40ddc099",
                  "platform":"ios",
                  "arn":"arn:aws:sns:us-east-1:853461911189:endpoint/APNS_SANDBOX/kid_W19oFN4H1l_development/5957aec1-d587-3d3c-b7ff-ed76f09e444a"
               }
            ]
         },
         "bio":"Studying at Winchester School of Art, Southampton University",
         "_push":{  
            "_devicetokens":[  
               "d66f9d002ff0f8b630408fa0d411aa3eb5d9ef946b9930cea1986bedf5a09d97"
            ]
         },
         "_acl":{  
            "creator":"55b3cf59eaf8fd4a0c024038"
         },
         "username":"darren",
         "_kmd":{  
            "lmt":"2016-03-18T19:12:09.099Z",
            "ect":"2015-07-25T18:03:05.741Z"
         },
         "first_name":"Darren Yu",
         "salt":"1ab54605-9bf1-4932-bbae-e375fda235df"
      },
      "details":"Welcome all. This week we are holding an event hosted by RAG our charity segment of SUSU! We are currently taking donations for a range of good causes so start February with some good KARMA and donate generously! \n\nLove,\n\nAnjit (Student Union Vice President)",
      "access":"public",
      "authorId":"55b3cf59eaf8fd4a0c024038",
      "pictureId":"e39ebe5c-f51a-4083-95c4-49665ace2594",
      "date":"ISODate(\"2016-02-04T11:33:05.324Z\")",
      "_acl":{  
         "creator":"55b3cf59eaf8fd4a0c024038"
      },
      "createdAt":"ISODate(\"2016-03-15T00:49:31.657Z\")",
      "_kmd":{  
         "lmt":"2016-07-29T00:32:24.988Z",
         "ect":"2016-02-04T11:36:42.340Z"
      },
      "name":"The BIG give! "
   },

  

The result from querying the collection in console is :

 

{  
   "_id":"56b337ca7540b68d6d00c59e",
   "author":{  
      "_type":"KinveyRef",
      "_id":"55b3cf59eaf8fd4a0c024038",
      "_collection":"user"
   },
   "details":"Welcome all. This week we are holding an event hosted by RAG our charity segment of SUSU! We are currently taking donations for a range of good causes so start February with some good KARMA and donate generously! \n\nLove,\n\nAnjit (Student Union Vice President)",
   "access":"public",
   "authorId":"55b3cf59eaf8fd4a0c024038",
   "pictureId":"e39ebe5c-f51a-4083-95c4-49665ace2594",
   "date":"ISODate(\"2016-02-04T11:33:05.324Z\")",
   "_acl":{  
      "creator":"55b3cf59eaf8fd4a0c024038"
   },
   "createdAt":"ISODate(\"2016-03-15T00:49:31.657Z\")",
   "_kmd":{  
      "lmt":"2016-03-15T00:49:31.663Z",
      "ect":"2016-02-04T11:36:42.340Z"
   },
   "name":"The BIG give! "
}

 

But it is only querying in the console, when I call queryWithQuery within the app, it returns relational data ( author of the event) as well.


The crash in the app happens when I try to cast results from endpoint from raw JSON into Event with the following line:


 for result in results as! [AnyObject]! {

     let event = result as! Event 

 }


However if the result is from completion block of KCSLinkedAppDataStore.queryWithQuery, same casting works fine.


Therefore I suggest in the first case response is raw JSON and in the second it's a KCSPersistable Object and that there is some private method provided by Kinvey that casts raw JSON into objects that conform KCSPersistable protocol


I don't really want to write that wrapper to handle this case, I consider it would be waste of time. Is there a method provided that can cast raw JSON into KCSPersistable objects, so that calling the endpoint would work the same way as KCSAppdataStore.queryWithQuery .



Also my Kid is  kid_W19oFN4H1l

Grisha,


Can you send the definition of your Event class?

Do you see any difference in the outputs (structure of the json) (when you run them from the Kinvey Console)?


Query 1) Output when querying the collection
Query 2) Output when executing the custom endpoint


Also, let me know the Kinvey sdk version.


Thanks,

Pranav

Kinvey Support

My replies seem to appear with a big delay, please see my responses above. Also my SDK version is 1.40.7

Grisha,

I was trying to replicate your issue. Are you getting this below error?
"Could not cast value of type '__NSCFDictionary' to ‘TestKinvey.Event’"


One way to deal with it to write an init() for the Event class that takes an NSDictionary as a parameter:

init(dict2: NSDictionary)
    {
// initialize variables here
        entityId =  dict2["_id"] as? NSString
      
        name = dict2["name"] as? NSString
        details = dict2["details"] as? NSString

    }

and then use that to construct your Events from raw json:
      let jobrl: Event_Grisha? = Event_Grisha(dict2: obj[0] as! NSDictionary)


But I understand that you want both the methods (using KCSLinkedAppdataStore as well as using KCSCustomEndpoint) to be similar. I am still reviewing this and I will get back to you with more information as soon as possible.


Thanks,
Pranav
Kinvey Support

 

Exactly this issue!

I would have done it with initializer (tried it before), however I also need to create KCSMetadata object and I don't know how to construct it and fill it with properties. Also I am intend to reuse these downloaded events, for example user downloads followed content, finds his own event and wants to edit some values in it and save to the server, however if I do it your way, I am not sure what will happen to metadata fields and will it save all other properties properly.


Kinvey already solved this problem somehow constructing these objects and providing them with completion block with KCSLinkedAppDataStore , would it be possible to just tell me how to replicate this way?

Login or Signup to post a comment