Skip to content

[ParseCloudFunction] Unhandled exception: type 'ParseObject' is not a subtype of type 'DietPlan' in type cast #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ghost opened this issue Jan 11, 2019 · 22 comments

Comments

@ghost
Copy link

ghost commented Jan 11, 2019

Hello,

I created a Cloud Code Function which returns only one Diet_Plans object.

I execute this code :

function() async {
    var function = ParseCloudFunction('getOneDietPlan');
    var response = await function.execute();

    if (response.success) {
      print(ApplicationConstants.APP_NAME + ": " + (response.result as DietPlan).name);
    } else {
      print(ApplicationConstants.APP_NAME + ": " + response.error.message);
    }
  }

And there is an error with the print in the success condition, this is my console :

I/flutter (25760): ----
I/flutter (25760): Hello Flutter API Response (getOneDietPlan : ParseApiRQ.execute) :
I/flutter (25760): Status Code: 200
I/flutter (25760): Payload: Instance of 'ParseObject'
I/flutter (25760): ----
E/flutter (25760): [ERROR:flutter/shell/common/shell.cc(184)] Dart Error: Unhandled exception:
E/flutter (25760): type 'ParseObject' is not a subtype of type 'DietPlan' in type cast

And similar error when the cloud code function return multiple objects, there is an error like :

type 'ParseObject' is not a subtype of type 'Iterable<dynamic>' in type cast

In advance, thank you.

@phillwiggins
Copy link
Member

Actually, thinking back, I'm not sure that this would work. There's no way for the function to know that you are working with DietPlans...

I'm not sure what the default functionality for this should be. Should it recognise that it's a DietPlan, or would it always recognise it as a ParseObject?

@ghost
Copy link
Author

ghost commented Jan 11, 2019

If you check the updated example there's some additional lines added to DietPlan.

I copy/pasted the updated example

Also, iterable usually means that it's a list of results rather than single
result. Which RQ was that from? Was you expecting a single result?

I know, I test with two cloud code functions, the first which returns one object,
the error was : type 'ParseObject' is not a subtype of type 'DietPlan' in type cast

And I test a second CC function which returns multiple objects and the error was :
type 'ParseObject' is not a subtype of type 'Iterable<dynamic>' in type cast

I'm not sure what the default functionality for this should be. Should it recognise that it's a DietPlan, or would it always recognise it as a ParseObject?

Sorry but I didn't understand :/

@phillwiggins
Copy link
Member

Okay, for a function result to be recognised as a custom class, your DietPlan would extend function, instead of ParseObject. The only other way you can do this is to create a method inside DietPlan, that converts a ParseObject to a DietPlan.

i.e.:-

ParseObject extends ParseFunction implements ParseCloneable {

DietPlan() : super(DIET_PLAN);
DietPlan.clone(): this();

@override clone(Map map) => DietPlan.clone()..fromJson(map);

static const String DIET_PLAN = 'Diet_Plans';
static const String NAME = 'Name';

String get name => get<String>(NAME);
set name(String name) => set<String>(NAME, name);

}

Or maybe something like inside DietPlan:-

static DietPlan convertParseObjectToDietPlan(ParseObject object) {
var dietPlan = DietPlan();
dietPlan.name = object.get<String>('name');
return dietPlan;

With regards to the 'ParseObject' is not a subtype of 'Iterable', it's because that is a list of ParseObjects, not a single object. You would need to create something like:-

List<ParseObject> objects = result;

Then using the method above, in DietPlan:-

List<DietPlan> dietPlans = List();

for (var object in objects) {
    dietPlans.add(DietPlan.convertParseObjectToDietPlan(object);
}

@phillwiggins
Copy link
Member

Closing issue. Please reopen if not resolved

@ghost
Copy link
Author

ghost commented Jan 14, 2019

Hi, I don't know how to reopen the issue but there is now an other problem.

I created a Cloud Code function which returns only one object (Web), this is my code ::

web.dart

import 'dart:core';

import 'package:parse_server_sdk/parse.dart';

class Web extends ParseObject implements ParseCloneable {
  Web() : super(_keyTableName);

  Web.clone() : this();

  /// Looks strangely hacky but due to Flutter not using reflection, we have to
  /// mimic a clone
  @override
  clone(Map map) => Web.clone()..fromJson(map);

  static const String _keyTableName = 'Web';
  static const String keySlogan = 'slogan';

  String get slogan => get<String>(keySlogan);

  set slogan(String name) => set<String>(keySlogan, name);

  static Web convertParseObjectToWeb(ParseObject object) {
    var web = Web();
    web.slogan = object.get<String>('slogan');
    return web;
  }
}

In my main.dart:

function() async {
    var function = ParseCloudFunction('webGet_v1');
    var response = await function.execute();

    if (response.success) {
      Web web = Web.convertParseObjectToWeb(response.result);
      print(ApplicationConstants.APP_NAME + ": " + web.toString());
    } else {
      print(ApplicationConstants.APP_NAME + ": " + response.error.message);
    }
  }

But after the print, in my console :

I/flutter (26953): ----
I/flutter (26953): Hello Flutter API Response (webGet_v1 : ParseApiRQ.execute) :
I/flutter (26953): Status Code: 200
I/flutter (26953): Payload: {"className":"className","result":"{'__type': 'Pointer', className: Web, objectId: 9oI0vcR3mt}"}
I/flutter (26953): ----
I/flutter (26953): Hello Flutter: {"className":"Web"}

The problem is that my Web object si almost empty, there is only the className but no other infos like the slogan (string in my object).

Do you have an idea ? In advance, thank you.

@phillwiggins phillwiggins reopened this Jan 14, 2019
@phillwiggins
Copy link
Member

Hey,

It looks to me that the object you are sending yourself from your Cloud Function is the issue.

The payload print statement in the ParseResult item is simple the response we receive from the server. It doesn't look like you are actually sending the correct object in your Cloud Function.... I think anyway?

I'm not an expert in the Cloud Function area in all honesty, but judging by your response, that suggests to me that your not sending the correct object.

@ghost
Copy link
Author

ghost commented Jan 14, 2019

Yes I'm sending the correct object but I couldn't say what's the problem in the SDK.

Maybe you can test it on your side with a basic cloud coud function which just returns the first object.

@phillwiggins
Copy link
Member

Sorry, I think you misunderstood...

Payload: {"className":"className","result":"{'__type': 'Pointer', className: Web, objectId: 9oI0vcR3mt}"}

is what the server is sending back, that's it. There's no more information for the SDK to work with. That is a reference to an object, but not an object itself. If this is the standard behaviour on the Android SDK or iOS SDK then I would have to implement another query based on this.

For example,

String objectId = response.result['obJectID'];
String className = response.result['className'];
ParseObject object = ParseObject(className);
object = await object.getObject(objectId);

Your cloud function is only sending a pointer, rather than the full object itself.

@ghost
Copy link
Author

ghost commented Jan 14, 2019

I understand what you say but, see my logs from Parse : (don't worry if it's no more a Web object but a News object, I'm making some test)

2019-01-14T14:08:58.325Z - Ran cloud function publicNewsFind_v1 for user undefined with:
  Input: {}
  Result: [{"releaseDate":{"__type":"Date","iso":"2019-01-14T08:00:10.791Z"},"title":"Lorem ipsum dolor sit amet.","content":"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>","createdAt":"2019-01-14T09:28:52.514Z","updatedAt":"2019-01-14T14:08:50.543Z","objectId":"MLuwZGpBG1","__type":"Object","className":"News"},{"title":"Test","createdAt":"2019-01-14T13:51:11.314Z","updatedAt":"2019-01-14T13:51:24.844Z","content":"1234","releaseDate":{"__type":"Date","iso":"2019-01-13T13:51:00.000Z"},"objectId":"1kLvWoO3i5","__type":"Object","className":"News"}]

And see my console :

I/flutter (30004): ----
I/flutter (30004): Hello Flutter API Response (publicNewsFind_v1 : ParseApiRQ.execute) :
I/flutter (30004): Status Code: 200
I/flutter (30004): Payload: {"className":"className","result":["{'__type': 'Pointer', className: News, objectId: MLuwZGpBG1}","{'__type': 'Pointer', className: News, objectId: 1kLvWoO3i5}"]}
I/flutter (30004): ----

@phillwiggins
Copy link
Member

I see, I'm still a bit confused though. What happens if you breakpoint the ParseResponse object. What does the ParseResponse.result object contain?

@ghost
Copy link
Author

ghost commented Jan 14, 2019

My code

if (response.success) {
      ParseResponse parseResponse = response;
      print("parseResponse.result.toString(): " + parseResponse.result.toString());
    }

Console:

I/flutter (30004): parseResponse.result.toString(): {"className":"className","result":["{'__type': 'Pointer', className: News, objectId: MLuwZGpBG1}","{'__type': 'Pointer', className: News, objectId: 1kLvWoO3i5}"]}

@phillwiggins
Copy link
Member

Okay, so yeah... I was correct above in saying the object has not been received. Only a pointer to the object has been received?

@ghost
Copy link
Author

ghost commented Jan 14, 2019

It looks like yes but the cloud code function returns an object and not a pointer (we can see it in the logs) :/

@phillwiggins
Copy link
Member

Which logs does it show an object, in the Cloud Function?

@ghost
Copy link
Author

ghost commented Jan 14, 2019

Yes

2019-01-14T14:08:58.325Z - Ran cloud function publicNewsFind_v1 for user undefined with:
  Input: {}
  Result: [{"releaseDate":{"__type":"Date","iso":"2019-01-14T08:00:10.791Z"},"title":"Lorem ipsum dolor sit amet.","content":"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>","createdAt":"2019-01-14T09:28:52.514Z","updatedAt":"2019-01-14T14:08:50.543Z","objectId":"MLuwZGpBG1","__type":"Object","className":"News"},{"title":"Test","createdAt":"2019-01-14T13:51:11.314Z","updatedAt":"2019-01-14T13:51:24.844Z","content":"1234","releaseDate":{"__type":"Date","iso":"2019-01-13T13:51:00.000Z"},"objectId":"1kLvWoO3i5","__type":"Object","className":"News"}]

@phillwiggins
Copy link
Member

Okay, so that's still technically correct.

The SDK is only printing what is received from the server... Which technically is the object, but not all the object data. It's only sending a reference to that object which you can obtain as you have the objectId. The SDK is only printing in payload, exactly what it receives from the server, no more and no less.

@ghost
Copy link
Author

ghost commented Jan 14, 2019

Ok I understand but what is the solution ?

Is that the SDK should made a query for each object (with its objectId) and store all complete objects in a list ?

@phillwiggins
Copy link
Member

Correct.

The cloud function should send a list of pointers (so that the payload is very minimal) and then the SDK should make requests for more information as per the example I gave above. this is the most efficient way to do what you are trying to achieve.

@ghost
Copy link
Author

ghost commented Jan 14, 2019

Ok so I imagine that it's a feature which will be implemented later in the sdk ?

@phillwiggins
Copy link
Member

No.

That's not a function the SDK should handle. That should be the implementation of the SDK.

@ghost
Copy link
Author

ghost commented Jan 14, 2019

Sorry for my bad english but I don't understand.

My question would be rather: Have I to handle this "problem" like I say :

...made a query for each object (with its objectId) and store all complete objects in a list...

Or later, the SDK will be able to manage this ?

PS: I take this opportunity to thank you Phill Wiggins for your work for the moment and I can't wait to see the Flutter Parse SDK evolve in the future. 🥇

@phillwiggins
Copy link
Member

I think that it's something we could look at in the future, but for now there are more urgent features that need developing.

Right now, if I was you, I would just make a query using the objectId's provided from your cloud function.

I hope that helps and your very welcome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant