GetDutchie / brick

An intuitive way to work with persistent data in Dart
https://getdutchie.github.io/brick/#/
361 stars 28 forks source link

bug(supabase-integration): OfflineFirstWithSupabaseAdapter fields are generated twice #416

Closed devj3ns closed 2 months ago

devj3ns commented 2 months ago

When running dart run build_runner build, the fields inside the generated OfflineFirstWithSupabaseAdapter are created twice, which results in the following error when running the app:

lib/brick/adapters/user_adapter.g.dart:164:16: Error: 'tableName' is already declared in this scope.
  final String tableName = 'User';

As seen in the full code below, the fields like tableName, defaultToNull, fieldsToSqliteColumns (...) are defined twice. Interesting is that they are not the same (e.g. final tableName = 'user'; and final String tableName = 'User';).

Full code of the OfflineFirstWithSupabaseAdapter ```dart /// Construct a [User] class UserAdapter extends OfflineFirstWithSupabaseAdapter { UserAdapter(); @override final tableName = 'user'; @override final defaultToNull = true; @override final Map fieldsToSqliteColumns = { 'id': const RuntimeSupabaseColumnDefinition( association: false, associationForeignKey: 'null', associationType: UUID, columnName: 'id', ), 'firstName': const RuntimeSupabaseColumnDefinition( association: false, associationForeignKey: 'null', associationType: String, columnName: 'first_name', ), 'lastName': const RuntimeSupabaseColumnDefinition( association: false, associationForeignKey: 'null', associationType: String, columnName: 'last_name', ), 'email': const RuntimeSupabaseColumnDefinition( association: false, associationForeignKey: 'null', associationType: String, columnName: 'email', ), 'role': const RuntimeSupabaseColumnDefinition( association: false, associationForeignKey: 'null', associationType: UserRole, columnName: 'role', ), 'deletedAt': const RuntimeSupabaseColumnDefinition( association: false, associationForeignKey: 'null', associationType: TimestampTz, columnName: 'deleted_at', ) }; @override final ignoreDuplicates = false; @override final uniqueFields = {}; @override final Map fieldsToSqliteColumns = { 'primaryKey': const RuntimeSqliteColumnDefinition( association: false, columnName: '_brick_id', iterable: false, type: int, ), 'id': const RuntimeSqliteColumnDefinition( association: false, columnName: 'id', iterable: false, type: UUID, ), 'firstName': const RuntimeSqliteColumnDefinition( association: false, columnName: 'first_name', iterable: false, type: String, ), 'lastName': const RuntimeSqliteColumnDefinition( association: false, columnName: 'last_name', iterable: false, type: String, ), 'email': const RuntimeSqliteColumnDefinition( association: false, columnName: 'email', iterable: false, type: String, ), 'role': const RuntimeSqliteColumnDefinition( association: false, columnName: 'role', iterable: false, type: UserRole, ), 'deletedAt': const RuntimeSqliteColumnDefinition( association: false, columnName: 'deleted_at', iterable: false, type: TimestampTz, ) }; @override Future primaryKeyByUniqueColumns( User instance, DatabaseExecutor executor) async { final results = await executor.rawQuery(''' SELECT * FROM `User` WHERE id = ? LIMIT 1''', [instance.id.toSqlite()]); // SQFlite returns [{}] when no results are found if (results.isEmpty || (results.length == 1 && results.first.isEmpty)) { return null; } return results.first['_brick_id'] as int; } @override final String tableName = 'User'; @override Future fromSupabase(Map input, {required provider, covariant OfflineFirstWithSupabaseRepository? repository}) async => await _$UserFromSupabase(input, provider: provider, repository: repository); @override Future> toSupabase(User input, {required provider, covariant OfflineFirstWithSupabaseRepository? repository}) async => await _$UserToSupabase(input, provider: provider, repository: repository); @override Future fromSqlite(Map input, {required provider, covariant OfflineFirstWithSupabaseRepository? repository}) async => await _$UserFromSqlite(input, provider: provider, repository: repository); @override Future> toSqlite(User input, {required provider, covariant OfflineFirstWithSupabaseRepository? repository}) async => await _$UserToSqlite(input, provider: provider, repository: repository); } ```
tshedor commented 2 months ago

Fixing in #417

The difference with the final String is a Flutter style I've since abandoned. It's fun to type strongly, but it gets tedious and unnecessary when it's inferred

tshedor commented 2 months ago

@devj3ns updated in brick_supabase: 0.1.1 and brick_supabase_generators: 0.1.1

devj3ns commented 2 months ago

@tshedor, thanks for the fix, unfortunately, there are some more issues with the adapter generation.

The name clash with the tableName got fixed by #417, but other attributes like fieldsToSqliteColumns still are generated twice, which results in the following runtime error:

lib/brick/adapters/user_adapter.g.dart:105:52: Error: 'fieldsToSqliteColumns' is already declared in this scope.
  final Map<String, RuntimeSqliteColumnDefinition> fieldsToSqliteColumns = {

It looks like the Map containing the RuntimeSupabaseColumnDefinitions has the wrong name, it should be:

final Map<String, RuntimeSupabaseColumnDefinition> fieldsToSupabaseColumns = {
    'id': const RuntimeSupabaseColumnDefinition(
    ...

instead of

final Map<String, RuntimeSqliteColumnDefinition> fieldsToSqliteColumns = {
    'id': const RuntimeSupabaseColumnDefinition(
    ...

Moreover, I just saw that the onConflict is missing its parenthesis:

@override
final onConflict = project_id;

And it is missing for adapters where its not defined in the SupabaseSerializable on the model annotation:

lib/brick/adapters/customer_adapter.g.dart:102:7: Error: The non-abstract class 'CustomerAdapter' is missing implementations for these members:
 - SupabaseAdapter.onConflict
Try to either
 - provide an implementation,
 - inherit an implementation from a superclass or mixin,
 - mark the class as abstract, or
 - provide a 'noSuchMethod' implementation.

I will try to create a PR which fixes this.

tshedor commented 2 months ago

@devj3ns Fixed and published

devj3ns commented 2 months ago

@tshedor You are too fast :D

I found one more problem with the onConflict (see edited comment above)

devj3ns commented 2 months ago

And the code $UserFromSupabaseand $UserToSupabase is missing the ID which uses my custom UUID OfflineFirstSerdes. I will provide more details in the hour (have to leave shortly).

tshedor commented 2 months ago

@devj3ns Fixed and published the fix for onConflict. I've got to get to bed. Appreciate your help, reporting, and patience.

devj3ns commented 2 months ago

I close this issue, as the naming conflict is resolved by https://github.com/GetDutchie/brick/pull/417 and https://github.com/GetDutchie/brick/pull/420

The onConflict problem is resolved by https://github.com/GetDutchie/brick/pull/422

The Serdes problem is resolved by https://github.com/GetDutchie/brick/pull/423