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.33k stars 248 forks source link

Bug in model generated for many to many mapping (Amplify Flutter) #1092

Closed aniket-chati closed 2 years ago

aniket-chati commented 2 years ago

Describe the bug The models generated for mapping tables in many-to-many relationships defined have a problem. Instead of defining fieldName in QueryField with an ID suffix, it gets defined as the table name. For example, for a many to many relationship between user and property table, the PropertyUser.dart contains fieldName as user and property - instead of userID and propertyID.

To Reproduce Steps to reproduce the behavior:

  1. Go to the admin console an create a user table and a property table.
  2. Create a many to many relationship between these.
  3. Deploy and pull the generated GraphQL files into the project's amplify CLI.
  4. Open the PropertyUser.dart file
  5. Check fieldName.

Expected behavior As explained earlier.

Screenshots Not applicable

Platform Amplify Flutter current supports iOS and Android. This issue is reproducible in (check all that apply): [X] Android [X] iOS

Output of flutter doctor -v Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel stable, 2.5.3, on macOS 12.0.1 21A559 darwin-x64, locale en-GB) [✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0) [✓] Xcode - develop for iOS and macOS [✓] Chrome - develop for the web [✓] Android Studio (version 2020.3) [✓] Connected device (2 available) • No issues found!
Dependencies (pubspec.lock) # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: amplify_analytics_plugin_interface: dependency: transitive description: name: amplify_analytics_plugin_interface url: "https://pub.dartlang.org" source: hosted version: "0.2.0" amplify_api: dependency: "direct main" description: name: amplify_api url: "https://pub.dartlang.org" source: hosted version: "0.2.0" amplify_api_plugin_interface: dependency: "direct main" description: name: amplify_api_plugin_interface url: "https://pub.dartlang.org" source: hosted version: "0.2.0" amplify_auth_cognito: dependency: "direct main" description: name: amplify_auth_cognito url: "https://pub.dartlang.org" source: hosted version: "0.2.0" amplify_auth_plugin_interface: dependency: transitive description: name: amplify_auth_plugin_interface url: "https://pub.dartlang.org" source: hosted version: "0.2.0" amplify_core: dependency: transitive description: name: amplify_core url: "https://pub.dartlang.org" source: hosted version: "0.2.0" amplify_datastore: dependency: "direct main" description: name: amplify_datastore url: "https://pub.dartlang.org" source: hosted version: "0.2.0" amplify_datastore_plugin_interface: dependency: transitive description: name: amplify_datastore_plugin_interface url: "https://pub.dartlang.org" source: hosted version: "0.2.0" amplify_flutter: dependency: "direct main" description: name: amplify_flutter url: "https://pub.dartlang.org" source: hosted version: "0.2.0" amplify_storage_plugin_interface: dependency: transitive description: name: amplify_storage_plugin_interface url: "https://pub.dartlang.org" source: hosted version: "0.2.0" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted version: "2.8.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted version: "2.1.0" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted version: "1.1.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted version: "1.3.1" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted version: "1.1.0" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted version: "1.15.0" convert: dependency: transitive description: name: convert url: "https://pub.dartlang.org" source: hosted version: "3.0.1" crypto: dependency: transitive description: name: crypto url: "https://pub.dartlang.org" source: hosted version: "3.0.1" cupertino_icons: dependency: "direct main" description: name: cupertino_icons url: "https://pub.dartlang.org" source: hosted version: "1.0.3" date_time_format: dependency: transitive description: name: date_time_format url: "https://pub.dartlang.org" source: hosted version: "2.0.1" event_bus: dependency: transitive description: name: event_bus url: "https://pub.dartlang.org" source: hosted version: "2.0.0" fake_async: dependency: transitive description: name: fake_async url: "https://pub.dartlang.org" source: hosted version: "1.2.0" fixnum: dependency: transitive description: name: fixnum url: "https://pub.dartlang.org" source: hosted version: "1.0.0" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" flutter_blue: dependency: "direct main" description: name: flutter_blue url: "https://pub.dartlang.org" source: hosted version: "0.8.0" flutter_lints: dependency: "direct dev" description: name: flutter_lints url: "https://pub.dartlang.org" source: hosted version: "1.0.4" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" lints: dependency: transitive description: name: lints url: "https://pub.dartlang.org" source: hosted version: "1.0.1" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted version: "0.12.10" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted version: "1.7.0" mqtt_client: dependency: "direct main" description: name: mqtt_client url: "https://pub.dartlang.org" source: hosted version: "9.6.1" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted version: "1.8.0" percent_indicator: dependency: "direct main" description: name: percent_indicator url: "https://pub.dartlang.org" source: hosted version: "3.4.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted version: "2.0.2" protobuf: dependency: transitive description: name: protobuf url: "https://pub.dartlang.org" source: hosted version: "2.0.0" rxdart: dependency: transitive description: name: rxdart url: "https://pub.dartlang.org" source: hosted version: "0.26.0" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" source_span: dependency: transitive description: name: source_span url: "https://pub.dartlang.org" source: hosted version: "1.8.1" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted version: "1.1.0" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted version: "1.2.0" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted version: "0.4.2" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted version: "1.3.0" uuid: dependency: transitive description: name: uuid url: "https://pub.dartlang.org" source: hosted version: "3.0.5" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted version: "2.1.0" sdks: dart: ">=2.12.0 <3.0.0" flutter: ">=1.20.0"

Smartphone (please complete the following information):

Additional context Add any other context about the problem here.

HuiSF commented 2 years ago

Hi @aniket-chati thanks for opening this issue.

In my impression this behavior is expected. When you look PropertyUser.dart, the user and property fields should be described as belongsTo User and Property respectively. It expects passing in a User object and Property objects, the underlying implementation can extra ids from the objects and assign to the underlying connection id fields.

Did you encounter any actual issue when you are manipulating data of these 3 models?

aniket-chati commented 2 years ago

Hello HuiSF,

Thanks for your prompt reply. I am indeed encountering actual issue.

Here is the code:

try {
      globalservices.propertyUserList = await Amplify.DataStore.query(PropertyUser.classType, where: PropertyUser.USER.eq(globalservices.user.id));
    } on DataStoreException catch (e) {
      print('Query to fetch property user failed: $e');
    }

Here is the error message (on Mi A2 / android):

E/amplify:flutter:datastore(27083): DataStoreException{message=Error in querying the model., cause=DataStoreException{message=Invalid SQL statement: SELECT `PropertyUser`.`id` AS `PropertyUser_id`, `PropertyUser`.`propertyID` AS `PropertyUser_propertyID`, `PropertyUser`.`userID` AS `PropertyUser_userID`, `Property`.`id` AS `Property_id`, `Property`.`address_line_1` AS `Property_address_line_1`, `Property`.`address_line_2` AS `Property_address_line_2`, `Property`.`city` AS `Property_city`, `Property`.`country` AS `Property_country`, `Property`.`pin_code` AS `Property_pin_code`, `Property`.`property_name` AS `Property_property_name`, `Property`.`state` AS `Property_state`, `User`.`id` AS `User_id`, `User`.`user_mobile_number` AS `User_user_mobile_number` FROM `PropertyUser` INNER JOIN `Property` ON `PropertyUser`.`propertyID`=`Property`.`id` INNER JOIN `User` ON `PropertyUser`.`userID`=`User`.`id` **WHERE user = ?;, cause=android.database.sqlite.SQLiteException: no such column: user (code 1 SQLITE_ERROR):** , while compiling: SELECT `PropertyUser`.`id` AS `PropertyUser_id`, `PropertyUser`.`propertyID` AS `PropertyUser_propertyID`, `PropertyUser`.`userID` AS `PropertyUser_userID`, `Property`.`id` AS `Property_id`, `Property`.`address_line_1` AS `Property_address_line_1`, `Property`.`address_line_2` AS `Property_address_line_2`, `Property`.`city` AS `Property_city`, `Property`.`country` AS `Property_country`, `Property`.`pin_code` AS `Property_pin_code`, `Property`.`property_name` AS `Property_property_name`, `Property`.`state` AS `Property_state`, `User`.`id` AS `User_id`, `User`.`user_mobile_number` AS `User_user_mobile_number` FROM `PropertyUser` INNER JOIN `Property` ON `PropertyUser`.`propertyID`=`Property`.`id` INNER JOIN `User` ON `PropertyUser`.`userID`=`User`.`id` **WHERE user = ?**;, recoverySuggestion=There is a possibility that there is a bug if this error persists. Please take a look at 
E/amplify:flutter:datastore(27083): https://github.com/aws-amplify/amplify-android/issues to see if there are any existing issues that 
E/amplify:flutter:datastore(27083): match your scenario, and file an issue with the details of the bug if there isn't.}, recoverySuggestion=See attached exception for details.}
E/amplify:flutter:datastore(27083):     at com.amplifyframework.datastore.storage.sqlite.SQLiteStorageAdapter.lambda$query$5$SQLiteStorageAdapter(SQLiteStorageAdapter.java:445)
E/amplify:flutter:datastore(27083):     at com.amplifyframework.datastore.storage.sqlite.-$$Lambda$SQLiteStorageAdapter$tej8E89aP-US0oj2jR3LG8zBWa8.run(Unknown Source:10)
E/amplify:flutter:datastore(27083):     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)
E/amplify:flutter:datastore(27083):     at java.util.concurrent.FutureTask.run(FutureTask.java:266)
E/amplify:flutter:datastore(27083):     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
E/amplify:flutter:datastore(27083):     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
E/amplify:flutter:datastore(27083):     at java.lang.Thread.run(Thread.java:919)
E/amplify:flutter:datastore(27083): Caused by: DataStoreException{message=Invalid SQL statement: SELECT `PropertyUser`.`id` AS `PropertyUser_id`, `PropertyUser`.`propertyID` AS `PropertyUser_propertyID`, `PropertyUser`.`userID` AS `PropertyUser_userID`, `Property`.`id` AS `Property_id`, `Property`.`address_line_1` AS `Property_address_line_1`, `Property`.`address_line_2` AS `Property_address_line_2`, `Property`.`city` AS `Property_city`, `Property`.`country` AS `Property_country`, `Property`.`pin_code` AS `Property_pin_code`, `Property`.`property_name` AS `Property_property_name`, `Property`.`state` AS `Property_state`, `User`.`id` AS `User_id`, `User`.`user_mobile_number` AS `User_user_mobile_number` FROM `PropertyUser` INNER JOIN `Property` ON `PropertyUser`.`propertyID`=`Property`.`id` INNER JOIN `User` ON `PropertyUser`.`userID`=`User`.`id` WHERE user = ?;, cause=android.database.sqlite.SQLiteException: no such column: user (code 1 SQLITE_ERROR): , while compiling: SELECT `PropertyUser`.`id` AS `PropertyUser_id`, `PropertyUser`.`propertyID` AS `PropertyUser_propertyID`, `PropertyUser`.`userID` AS `PropertyUser_userID`, `Property`.`id` AS `Property_id`, `Property`.`address_line_1` AS `Property_address_line_1`, `Property`.`address_line_2` AS `Property_address_line_2`, `Property`.`city` AS `Property_city`, `Property`.`country` AS `Property_country`, `Property`.`pin_code` AS `Property_pin_code`, `Property`.`property_name` AS `Property_property_name`, `Property`.`state` AS `Property_state`, `User`.`id` AS `User_id`, `User`.`user_mobile_number` AS `User_user_mobile_number` FROM `PropertyUser` INNER JOIN `Property` ON `PropertyUser`.`propertyID`=`Property`.`id` INNER JOIN `User` ON `PropertyUser`.`userID`=`User`.`id` WHERE user = ?;, recoverySuggestion=There is a possibility that there is a bug if this error persists. Please take a look at 
E/amplify:flutter:datastore(27083): https://github.com/aws-amplify/amplify-android/issues to see if there are any existing issues that 
E/amplify:flutter:datastore(27083): match your scenario, and file an issue with the details of the bug if there isn't.}
E/amplify:flutter:datastore(27083):     at com.amplifyframework.datastore.storage.sqlite.SQLCommandProcessor.dataStoreException(SQLCommandProcessor.java:86)
E/amplify:flutter:datastore(27083):     at com.amplifyframework.datastore.storage.sqlite.SQLCommandProcessor.rawQuery(SQLCommandProcessor.java:54)
E/amplify:flutter:datastore(27083):     at com.amplifyframework.datastore.storage.sqlite.SQLiteStorageAdapter.lambda$query$5$SQLiteStorageAdapter(SQLiteStorageAdapter.java:403)
E/amplify:flutter:datastore(27083):     ... 6 more
E/amplify:flutter:datastore(27083): Caused by: android.database.sqlite.SQLiteException: no such column: user (code 1 SQLITE_ERROR): , while compiling: SELECT `PropertyUser`.`id` AS `PropertyUser_id`, `PropertyUser`.`propertyID` AS `PropertyUser_propertyID`, `PropertyUser`.`userID` AS `PropertyUser_userID`, `Property`.`id` AS `Property_id`, `Property`.`address_line_1` AS `Property_address_line_1`, `Property`.`address_line_2` AS `Property_address_line_2`, `Property`.`city` AS `Property_city`, `Property`.`country` AS `Property_country`, `Property`.`pin_code` AS `Property_pin_code`, `Property`.`property_name` AS `Property_property_name`, `Property`.`state` AS `Property_state`, `User`.`id` AS `User_id`, `User`.`user_mobile_number` AS `User_user_mobile_number` FROM `PropertyUser` INNER JOIN `Property` ON `PropertyUser`.`propertyID`=`Property`.`id` INNER JOIN `User` ON `PropertyUser`.`userID`=`User`.`id` WHERE user = ?;
E/amplify:flutter:datastore(27083):     at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
E/amplify:flutter:datastore(27083):     at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:986)
E/amplify:flutter:datastore(27083):     at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:593)
E/amplify:flutter:datastore(27083):     at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:590)
E/amplify:flutter:datastore(27083):     at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:61)
E/amplify:flutter:datastore(27083):     at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
E/amplify:flutter:datastore(27083):     at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:46)
E/amplify:flutter:datastore(27083):     at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1443)
E/amplify:flutter:datastore(27083):     at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1382)
E/amplify:flutter:datastore(27083):     at com.amplifyframework.datastore.storage.sqlite.SQLCommandProcessor.rawQuery(SQLCommandProcessor.java:49)
E/amplify:flutter:datastore(27083):     ... 7 more

Here is a snippet from the PropertyUser Class generated by Amplify:

  static final QueryField ID = QueryField(fieldName: "propertyUser.id");
  static final QueryField PROPERTY = QueryField(
    fieldName: "property",
    fieldType: ModelFieldType(ModelFieldTypeEnum.model, ofModelName: (Property).toString()));
  static final QueryField USER = QueryField(
    fieldName: "user",
    fieldType: ModelFieldType(ModelFieldTypeEnum.model, ofModelName: (User).toString()));
  static var schema = Model.defineSchema(define: (ModelSchemaDefinition modelSchemaDefinition) {
    modelSchemaDefinition.name = "PropertyUser";
    modelSchemaDefinition.pluralName = "PropertyUsers";

    modelSchemaDefinition.authRules = [
      AuthRule(
        authStrategy: AuthStrategy.PUBLIC,
        operations: [
          ModelOperation.CREATE,
          ModelOperation.UPDATE,
          ModelOperation.DELETE,
          ModelOperation.READ
        ]),
      AuthRule(
        authStrategy: AuthStrategy.PRIVATE,
        operations: [
          ModelOperation.CREATE,
          ModelOperation.UPDATE,
          ModelOperation.DELETE,
          ModelOperation.READ
        ]),
      AuthRule(
        authStrategy: AuthStrategy.PUBLIC,
        operations: [
          ModelOperation.CREATE,
          ModelOperation.UPDATE,
          ModelOperation.DELETE,
          ModelOperation.READ
        ])
    ];

    modelSchemaDefinition.addField(ModelFieldDefinition.id());

    modelSchemaDefinition.addField(ModelFieldDefinition.belongsTo(
      key: PropertyUser.PROPERTY,
      isRequired: true,
      targetName: "propertyID",
      ofModelName: (Property).toString()
    ));

    modelSchemaDefinition.addField(ModelFieldDefinition.belongsTo(
      key: PropertyUser.USER,
      isRequired: true,
      targetName: "userID",
      ofModelName: (User).toString()
    ));
  });
HuiSF commented 2 years ago

Thanks for providing the info @aniket-chati I think you are facing the same issue described in this issue https://github.com/aws-amplify/amplify-flutter/issues/444 . Which has already been fixed since version 0.2.2.

I noticed that you are still using version 0.2.0 could you try to upgrade to the latest version see if this issue persists?

offlineprogrammer commented 2 years ago

Hey @aniket-chati

I am closing this issue for now as we didn't hear from you following the above comment from @HuiSF We can reopen it if you are still facing the issue.

Regards Mo