aws-amplify / amplify-flutter

A declarative library with an easy-to-use interface for building Flutter applications on AWS.
https://docs.amplify.aws
Apache License 2.0
1.32k stars 247 forks source link

Unable to save models containing multiple belongsTo fields. #2073

Closed ignitum-ryan closed 2 years ago

ignitum-ryan commented 2 years ago

Description

When attempting to create new models that contain multiple belongsTo annotations, it is returning the error 'Variable 'input' has coerced Null value for NonNull type 'ID!'

Here are the model schemas being used:

type Friendship @model @auth(rules: [{allow: private}]) {
  id: ID!
  status: FriendStatus!
  senderID: String
  receiverID: String
  users: [Friend] @hasMany(indexName: "byFriendship", fields: ["id"])
}

type Friend @model @auth(rules: [{allow: private}]) {
  id: ID!
  userID: ID! @index(name: "byUser")
  user: User! @belongsTo(fields: ["userID"])
  friendshipID: ID! @index(name: "byFriendship")
  friendship: Friendship! @belongsTo(fields: ["friendshipID"])
}

type User @model @auth(rules: [{allow: private}]) {
  id: ID!
  displayName: String!
  friends: [Friend] @hasMany(indexName: "byUser", fields: ["id"])
}

The code used to create the models and attempt to save in the database:

 Friendship newFriendRequest = Friendship(
          receiverID: receivingUser.id,
          senderID: currentUser.id,
          status: FriendStatus.Pending);
 Friend sender = Friend(friendship: newFriendRequest, user: currentUser);
 Friend receiver = Friend(friendship: newFriendRequest, user: receivingUser);
 newFriendRequest = newFriendRequest.copyWith(users: [sender, receiver]);

 Friendship? savedFriendRequest = await saveNewFriendship(newFriendRequest);
 Friend? savedSender = await saveNewFriend(sender);
 Friend? savedReceiver = await saveNewFriend(receiver);

  Future<Friend?> saveNewFriend(Friend friendToSave) async {
    try {
      final request = ModelMutations.create(friendToSave); // LINE A
      final response = await Amplify.API.mutate(request: request).response; // LINE B
      if (response.errors.isNotEmpty) {
        print('Could not save new friend: Response: ${response.errors}');
        throw(response.errors);
      }
      return response.data;
    } catch (error) {
      print('Could not save new friend: ${error}');
      Sentry.captureException(error, hint: 'Could not save new friend.');
    }
    return null;
  }

  Future<Friendship?> saveNewFriendship(Friendship newFriendship) async {
    try {
      final request = ModelMutations.create(newFriendship);
      final response = await Amplify.API.mutate(request: request).response;
      if (response.errors.isNotEmpty) {
        print('Could not save new friendship: Response: ${response.errors}');
        throw(response.errors);
      }
      return response.data;
    } catch (error) {
      print('Could not save new friendship: ${error}');
      Sentry.captureException(error, hint: 'Could not save new friendship.');
    }
    return null;
  }

The saving of the Friendship is successful, however, for each of the saving of the friends, the response returns a GraphQL error stating: 'Variable 'input' has coerced Null value for NonNull type 'ID!'

The reason I believe it has to do with the multiple belongsTo annotations is due to the ordering of the Friend model. The model coming into the saveNewFriend function appears correctly (Notated above as 'LINE A'), meaning it has all of the fields populated and the correct relationship. (See screenshots below). However, once the request object is returned from the ModelMutations.create() function, it DROPS the friendshipID (also in screenshots below). If I go to the schema.graphql file and SWAP the positions of the user/userID field with the friendship/friendshipID field, then the request object returned will DROP the userID resulting in the same error.

According to the documentation found here, it specifies that multiple belongsTo annotations should be allowed.

Categories

Steps to Reproduce

  1. Copy the models above in an Amplify Flutter project with API (GraphQL)
  2. Create the models in the dart code.
  3. Save the Friendship first.
  4. Attempt to Save the Friend objects.
  5. (Error should occur stating that the coerced Null value for ID!)

Screenshots

The friendToSave object being passed into the saveNewFriend method: image

Above, you can see that both the User field and the Friendship field is populated in the Friend object.

The request object from 'LINE A' above: image

After going through the ModelMutation.create() function, it drops the friendshipID field and the only inputs remaining are the ID from the Friendship, and the ID of the user field.

Here's the response of the call followed by the error: image

Platforms

Android Device/Emulator API Level

API 32+

Environment

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.0.5, on macOS 12.5 21G72 darwin-x64, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 13.4.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.2)
[✓] IntelliJ IDEA Community Edition (version 2018.3.3)
[✓] VS Code (version 1.69.2)
[✓] Connected device (4 available)
[✓] HTTP Host Availability

Dependencies

Dart SDK 2.17.6
Flutter SDK 3.0.5
gtg_mobile 0.0.1+4

dependencies:
- algolia 1.1.1 [meta http uuid universal_io]
- amplify_api 0.6.2 [amplify_api_android amplify_api_ios amplify_core amplify_flutter aws_common collection flutter meta plugin_platform_interface]
- amplify_auth_cognito 0.6.2 [amplify_auth_cognito_android amplify_auth_cognito_ios amplify_core aws_common collection flutter meta plugin_platform_interface]
- amplify_datastore 0.6.2 [flutter amplify_datastore_plugin_interface amplify_core plugin_platform_interface meta collection async]
- amplify_flutter 0.6.2 [amplify_core amplify_datastore_plugin_interface amplify_flutter_android amplify_flutter_ios aws_common collection flutter meta plugin_platform_interface]
- amplify_storage_s3 0.6.2 [amplify_storage_s3_android amplify_storage_s3_ios amplify_core aws_common flutter meta plugin_platform_interface]
- cached_network_image 3.2.1 [flutter flutter_cache_manager octo_image cached_network_image_platform_interface cached_network_image_web]
- carousel_slider 4.1.1 [flutter]
- crop_your_image 0.7.2 [flutter image]
- cupertino_icons 1.0.5
- dash_chat_2 0.0.12 [cached_network_image flutter flutter_parsed_text intl url_launcher video_player]
- detectable_text_field 2.0.3 [flutter]
- dropdown_search 5.0.2 [flutter]
- faker 2.0.0 [crypto]
- flutter 0.0.0 [characters collection material_color_utilities meta vector_math sky_engine]
- flutter_contacts 1.1.5 [flutter]
- flutter_dotenv 5.0.2 [flutter]
- flutter_form_builder 7.4.0 [flutter intl collection]
- flutter_signin_button 2.0.0 [flutter font_awesome_flutter]
- flutter_typeahead 4.0.0 [flutter flutter_keyboard_visibility]
- get 4.6.5 [flutter]
- google_fonts 3.0.1 [flutter http path_provider crypto]
- image_picker 0.8.5+3 [flutter image_picker_android image_picker_for_web image_picker_ios image_picker_platform_interface]
- json_annotation 4.6.0 [meta]
- modal_bottom_sheet 2.1.0 [flutter]
- path_provider 2.0.11 [flutter path_provider_android path_provider_ios path_provider_linux path_provider_macos path_provider_platform_interface path_provider_windows]
- sentry_flutter 6.6.3 [flutter flutter_web_plugins sentry package_info_plus meta]
- shimmer 2.0.0 [flutter]
- url_launcher 6.1.5 [flutter url_launcher_android url_launcher_ios url_launcher_linux url_launcher_macos url_launcher_platform_interface url_launcher_web url_launcher_windows]
- uuid 3.0.6 [crypto]
- webview_flutter 3.0.4 [flutter webview_flutter_android webview_flutter_platform_interface webview_flutter_wkwebview]

transitive dependencies:
- amplify_api_android 0.6.2 [flutter]
- amplify_api_ios 0.6.2 [amplify_core flutter]
- amplify_auth_cognito_android 0.6.2 [flutter]
- amplify_auth_cognito_ios 0.6.2 [amplify_core flutter]
- amplify_core 0.6.2 [aws_common collection flutter intl json_annotation meta plugin_platform_interface uuid]
- amplify_datastore_plugin_interface 0.6.2 [amplify_core collection flutter meta]
- amplify_flutter_android 0.6.2 [flutter]
- amplify_flutter_ios 0.6.2 [amplify_core flutter]
- amplify_storage_s3_android 0.6.2 [flutter]
- amplify_storage_s3_ios 0.6.2 [flutter]
- archive 3.3.1 [crypto path]
- async 2.9.0 [collection meta]
- aws_common 0.1.1 [async collection http meta stream_transform uuid]
- cached_network_image_platform_interface 1.0.0 [flutter flutter_cache_manager]
- cached_network_image_web 1.0.1 [flutter flutter_cache_manager cached_network_image_platform_interface]
- characters 1.2.0
- clock 1.1.1
- collection 1.16.0
- cross_file 0.3.3+1 [js meta]
- crypto 3.0.2 [typed_data]
- csslib 0.17.2 [source_span]
- ffi 1.2.1
- file 6.1.2 [meta path]
- flutter_blurhash 0.7.0 [flutter]
- flutter_cache_manager 3.3.0 [clock collection file flutter http path path_provider pedantic rxdart sqflite uuid]
- flutter_keyboard_visibility 5.3.0 [meta flutter_keyboard_visibility_platform_interface flutter_keyboard_visibility_web flutter]
- flutter_keyboard_visibility_platform_interface 2.0.0 [flutter meta plugin_platform_interface]
- flutter_keyboard_visibility_web 2.0.0 [flutter_keyboard_visibility_platform_interface flutter_web_plugins flutter]
- flutter_parsed_text 2.2.1 [flutter]
- flutter_plugin_android_lifecycle 2.0.7 [flutter]
- flutter_web_plugins 0.0.0 [flutter js characters collection material_color_utilities meta vector_math]
- font_awesome_flutter 9.2.0 [flutter]
- html 0.15.0 [csslib source_span]
- http 0.13.4 [async http_parser meta path]
- http_parser 4.0.1 [collection source_span string_scanner typed_data]
- image 3.2.0 [archive meta xml]
- image_picker_android 0.8.5+1 [flutter flutter_plugin_android_lifecycle image_picker_platform_interface]
- image_picker_for_web 2.1.8 [flutter flutter_web_plugins image_picker_platform_interface]
- image_picker_ios 0.8.5+6 [flutter image_picker_platform_interface]
- image_picker_platform_interface 2.5.0 [cross_file flutter http plugin_platform_interface]
- intl 0.17.0 [clock path]
- js 0.6.4
- material_color_utilities 0.1.4
- meta 1.7.0
- octo_image 1.0.2 [flutter flutter_blurhash]
- package_info_plus 1.4.2 [flutter package_info_plus_platform_interface package_info_plus_linux package_info_plus_macos package_info_plus_windows package_info_plus_web]
- package_info_plus_linux 1.0.5 [package_info_plus_platform_interface flutter path]
- package_info_plus_macos 1.3.0 [flutter]
- package_info_plus_platform_interface 1.0.2 [flutter meta plugin_platform_interface]
- package_info_plus_web 1.0.5 [flutter flutter_web_plugins http meta package_info_plus_platform_interface]
- package_info_plus_windows 1.0.5 [package_info_plus_platform_interface ffi flutter win32]
- path 1.8.2
- path_provider_android 2.0.16 [flutter path_provider_platform_interface]
- path_provider_ios 2.0.10 [flutter path_provider_platform_interface]
- path_provider_linux 2.1.7 [ffi flutter path path_provider_platform_interface xdg_directories]
- path_provider_macos 2.0.6 [flutter path_provider_platform_interface]
- path_provider_platform_interface 2.0.4 [flutter platform plugin_platform_interface]
- path_provider_windows 2.0.7 [ffi flutter path path_provider_platform_interface win32]
- pedantic 1.11.1
- petitparser 5.0.0 [meta]
- platform 3.1.0
- plugin_platform_interface 2.1.2 [meta]
- process 4.2.4 [file path platform]
- rxdart 0.27.5
- sentry 6.6.3 [http meta stack_trace uuid]
- sky_engine 0.0.99
- source_span 1.9.0 [collection path term_glyph]
- sqflite 2.0.3 [flutter sqflite_common path]
- sqflite_common 2.2.1+1 [synchronized path meta]
- stack_trace 1.10.0 [path]
- stream_transform 2.0.0
- string_scanner 1.1.1 [source_span]
- synchronized 3.0.0+2
- term_glyph 1.2.1
- typed_data 1.3.1 [collection]
- universal_io 2.0.4 [collection crypto meta typed_data]
- url_launcher_android 6.0.17 [flutter url_launcher_platform_interface]
- url_launcher_ios 6.0.17 [flutter url_launcher_platform_interface]
- url_launcher_linux 3.0.1 [flutter url_launcher_platform_interface]
- url_launcher_macos 3.0.1 [flutter url_launcher_platform_interface]
- url_launcher_platform_interface 2.1.0 [flutter plugin_platform_interface]
- url_launcher_web 2.0.12 [flutter flutter_web_plugins url_launcher_platform_interface]
- url_launcher_windows 3.0.1 [flutter url_launcher_platform_interface]
- vector_math 2.1.2
- video_player 2.4.5 [flutter html video_player_android video_player_avfoundation video_player_platform_interface video_player_web]
- video_player_android 2.3.8 [flutter video_player_platform_interface]
- video_player_avfoundation 2.3.5 [flutter video_player_platform_interface]
- video_player_platform_interface 5.1.3 [flutter plugin_platform_interface]
- video_player_web 2.0.12 [flutter flutter_web_plugins video_player_platform_interface]
- webview_flutter_android 2.9.0 [flutter webview_flutter_platform_interface]
- webview_flutter_platform_interface 1.9.1 [flutter meta plugin_platform_interface]
- webview_flutter_wkwebview 2.9.1 [flutter path webview_flutter_platform_interface]
- win32 2.6.1 [ffi]
- xdg_directories 0.2.0+1 [meta path process]
- xml 6.1.0 [collection meta petitparser]

Device

N/A

OS

N/A

Deployment Method

Amplify CLI

CLI Version

9.2.1

Additional Context

No response

Amplify Config

{ "UserAgent": "aws-amplify-cli/2.0", "Version": "1.0", "api": { "plugins": { "awsAPIPlugin": { "": { "endpointType": "GraphQL", "endpoint": "", "region": "us-east-2", "authorizationType": "AMAZON_COGNITO_USER_POOLS" } } } }, "auth": { "plugins": { "awsCognitoAuthPlugin": { "UserAgent": "aws-amplify-cli/0.1.0", "Version": "0.1.0", "IdentityManager": { "Default": {} }, "CredentialsProvider": { "CognitoIdentity": { "Default": { "PoolId": "", "Region": "us-east-2" } } }, "CognitoUserPool": { "Default": { "PoolId": "", "AppClientId": "", "Region": "us-east-2" } }, "Auth": { "Default": { "OAuth": { "WebDomain": "", "AppClientId": "", "SignInRedirectURI": "", "SignOutRedirectURI": "", "Scopes": [ "phone", "email", "openid", "profile", "aws.cognito.signin.user.admin" ] }, "authenticationFlowType": "USER_SRP_AUTH", "socialProviders": [ "FACEBOOK", "GOOGLE" ], "usernameAttributes": [ "EMAIL" ], "signupAttributes": [ "EMAIL", "NAME" ], "passwordProtectionSettings": { "passwordPolicyMinLength": 8, "passwordPolicyCharacters": [] }, "mfaConfiguration": "OPTIONAL", "mfaTypes": [ "SMS" ], "verificationMechanisms": [ "EMAIL" ] } }, "AppSync": { "Default": { "ApiUrl": "", "Region": "us-east-2", "AuthMode": "AMAZON_COGNITO_USER_POOLS", "ClientDatabasePrefix": "_AMAZON_COGNITO_USER_POOLS" }, "_AWS_IAM": { "ApiUrl": "", "Region": "us-east-2", "AuthMode": "AWS_IAM", "ClientDatabasePrefix": "_AWS_IAM" } }, "S3TransferUtility": { "Default": { "Bucket": "", "Region": "us-east-2" } } } } }, "storage": { "plugins": { "awsS3StoragePlugin": { "bucket": "", "region": "us-east-2", "defaultAccessLevel": "guest" } } } }

haverchuck commented 2 years ago

@ignitum-ryan - Thanks for posting this issue. I believe it is a duplicate of this issue. Please check there for updates.

ignitum-ryan commented 2 years ago

Contributor

Thanks for the reply @haverchuck . The issue does seem similar. The only piece that I'm not sure about is that the issue you linked has no mention of the varying 'ID!' fields being dropped from the ModelMutations.create() function. This could be semantics, I just want to make sure that the other ticket notes that it's impossible to create records that contain the multiple 'belongsTo' annotated fields.

Thanks again.

loami225 commented 2 years ago

thanks you @ignitum-ryan