From 7fb5f118e975dc9c5dc670582bb9fc4c37fac682 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 3 Mar 2024 13:05:44 +0000 Subject: [PATCH 01/50] wip: allow schema generation from types --- packages/yaroorm/lib/migration.dart | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/yaroorm/lib/migration.dart b/packages/yaroorm/lib/migration.dart index 070534f8..02ce66a7 100644 --- a/packages/yaroorm/lib/migration.dart +++ b/packages/yaroorm/lib/migration.dart @@ -180,6 +180,29 @@ class Schema { String toScript(TableBlueprint table) => _bluePrintFunc!.call(table).createScript(tableName); + static Schema fromEntity(Type entity) { + final meta = getEntityMetaData(entity); + final props = getEntityProperties(entity); + final tableName = getEntityTableName(entity); + + TableBlueprint make(TableBlueprint table, EntityPropertyData data) { + return switch (data.type) { + const (int) => table..integer(data.dbColumnName), + const (double) || const (num) => table..double(data.dbColumnName), + const (DateTime) => table..datetime(data.dbColumnName), + _ => table..string(data.dbColumnName), + }; + } + + return Schema.create(tableName, (table) { + table = table..id(name: meta.primaryKey); + for (final entry in props.entries) { + table = make(table, entry.value); + } + return table; + }); + } + static Schema create(String name, TableBluePrintFunc func) => Schema._(name, func); From 7b50eaf137e303066d2ec93447cc54b47790b67d Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 3 Mar 2024 15:39:18 +0000 Subject: [PATCH 02/50] compose schema from entity type --- packages/yaroorm/lib/migration.dart | 84 +++++++++++-------- .../lib/src/database/driver/pgsql_driver.dart | 21 +++-- .../lib/src/database/entity/entity.dart | 24 +++--- .../yaroorm/lib/src/reflection/reflector.dart | 50 ++++++++--- packages/yaroorm/pubspec.yaml | 6 +- .../test/integration/fixtures/migrations.dart | 26 +++--- .../test/integration/fixtures/test_data.dart | 6 +- .../test/unit/dialects/sqlite_test.dart | 2 +- pubspec.lock | 14 +--- 9 files changed, 137 insertions(+), 96 deletions(-) diff --git a/packages/yaroorm/lib/migration.dart b/packages/yaroorm/lib/migration.dart index 02ce66a7..4c8fa36c 100644 --- a/packages/yaroorm/lib/migration.dart +++ b/packages/yaroorm/lib/migration.dart @@ -36,9 +36,10 @@ abstract class TableBlueprint { void blob(String name, {bool nullable = false, String? defaultValue}); - void timestamps( - {String createdAt = entityCreatedAtColumnName, - String updatedAt = entityUpdatedAtColumnName}); + void timestamps({ + String createdAt = entityCreatedAtColumnName, + String updatedAt = entityUpdatedAtColumnName, + }); /// NUMBER TYPES /// ---------------------------------------------------------------- @@ -181,23 +182,25 @@ class Schema { _bluePrintFunc!.call(table).createScript(tableName); static Schema fromEntity(Type entity) { - final meta = getEntityMetaData(entity); - final props = getEntityProperties(entity); - final tableName = getEntityTableName(entity); - - TableBlueprint make(TableBlueprint table, EntityPropertyData data) { + make(TableBlueprint table, EntityPropertyData data) { return switch (data.type) { - const (int) => table..integer(data.dbColumnName), - const (double) || const (num) => table..double(data.dbColumnName), - const (DateTime) => table..datetime(data.dbColumnName), - _ => table..string(data.dbColumnName), + const (int) => table + ..integer(data.dbColumnName, nullable: data.nullable), + const (double) || const (num) => table + ..double(data.dbColumnName, nullable: data.nullable), + const (DateTime) => table + ..datetime(data.dbColumnName, nullable: data.nullable), + _ => table..string(data.dbColumnName, nullable: data.nullable), }; } - return Schema.create(tableName, (table) { - table = table..id(name: meta.primaryKey); - for (final entry in props.entries) { - table = make(table, entry.value); + final props = getEntityProperties(entity); + final primaryKey = props.values.firstWhere((e) => e.primaryKey); + + return Schema.create(getEntityTableName(entity), (table) { + table.id(name: primaryKey.dbColumnName); + for (final prop in props.values.where((e) => !e.primaryKey)) { + make(table, prop); } return table; }); @@ -206,7 +209,10 @@ class Schema { static Schema create(String name, TableBluePrintFunc func) => Schema._(name, func); - static Schema dropIfExists(String name) => _DropSchema(name); + static Schema dropIfExists(dynamic value) { + if (value is! String) value = getEntityTableName(value); + return _DropSchema(value); + } static Schema rename(String from, String to) => _RenameSchema(from, to); } @@ -267,22 +273,30 @@ class ForeignKey { this.constraint, }); - ForeignKey actions( - {ForeignKeyAction? onUpdate, ForeignKeyAction? onDelete}) => - ForeignKey(table, column, - foreignTable: foreignTable, - foreignTableColumn: foreignTableColumn, - nullable: nullable, - constraint: constraint, - onUpdate: onUpdate ?? this.onUpdate, - onDelete: onDelete ?? this.onDelete); - - ForeignKey constrained({String? name}) => ForeignKey(table, column, - foreignTable: foreignTable, - foreignTableColumn: foreignTableColumn, - nullable: nullable, - onUpdate: onUpdate, - onDelete: onDelete, - constraint: name ?? - 'fk_${table}_${column}_to_${foreignTable}_$foreignTableColumn'); + ForeignKey actions({ + ForeignKeyAction? onUpdate, + ForeignKeyAction? onDelete, + }) => + ForeignKey( + table, + column, + foreignTable: foreignTable, + foreignTableColumn: foreignTableColumn, + nullable: nullable, + constraint: constraint, + onUpdate: onUpdate ?? this.onUpdate, + onDelete: onDelete ?? this.onDelete, + ); + + ForeignKey constrained({String? name}) => ForeignKey( + table, + column, + foreignTable: foreignTable, + foreignTableColumn: foreignTableColumn, + nullable: nullable, + onUpdate: onUpdate, + onDelete: onDelete, + constraint: name ?? + 'fk_${table}_${column}_to_${foreignTable}_$foreignTableColumn', + ); } diff --git a/packages/yaroorm/lib/src/database/driver/pgsql_driver.dart b/packages/yaroorm/lib/src/database/driver/pgsql_driver.dart index d0fb1ad3..6f9b1e6a 100644 --- a/packages/yaroorm/lib/src/database/driver/pgsql_driver.dart +++ b/packages/yaroorm/lib/src/database/driver/pgsql_driver.dart @@ -285,18 +285,25 @@ class PgSqlTableBlueprint extends MySqlDriverTableBlueprint { } @override - void float(String name, - {bool nullable = false, num? defaultValue, int? precision, int? scale}) { + void float( + String name, { + bool nullable = false, + num? defaultValue, + int? precision, + int? scale, + }) { statements.add(makeColumn(name, 'DOUBLE PRECISION', nullable: nullable, defaultValue: defaultValue)); } @override - void double(String name, - {bool nullable = false, - num? defaultValue, - int? precision = 10, - int? scale = 0}) { + void double( + String name, { + bool nullable = false, + num? defaultValue, + int? precision = 10, + int? scale = 0, + }) { statements.add(makeColumn(name, 'NUMERIC($precision, $scale)', nullable: nullable, defaultValue: defaultValue)); } diff --git a/packages/yaroorm/lib/src/database/entity/entity.dart b/packages/yaroorm/lib/src/database/entity/entity.dart index b8497115..ca69eaf5 100644 --- a/packages/yaroorm/lib/src/database/entity/entity.dart +++ b/packages/yaroorm/lib/src/database/entity/entity.dart @@ -35,9 +35,9 @@ abstract class Entity { DateTime? updatedAt; - EntityMeta? _entityMetaCache; + Table? _entityMetaCache; - EntityMeta get entityMeta => _entityMetaCache ??= getEntityMetaData(Model); + Table get entityMeta => _entityMetaCache ??= getEntityMetaData(Model); DriverContract _driver = DB.defaultDriver; @@ -60,7 +60,7 @@ abstract class Entity { Future save() async { if (_isLoadedFromDB) { assert(id != null, 'Id cannot be null when loaded from database'); - if (entityMeta.timestamps) updatedAt = DateTime.now().toUtc(); + if (entityMeta.enableTimestamps) updatedAt = DateTime.now().toUtc(); await query.update(where: _whereId, values: to_db_data).execute(); return this as Model; } @@ -73,7 +73,7 @@ abstract class Entity { // ignore: non_constant_identifier_names Map get to_db_data { - if (entityMeta.timestamps) { + if (entityMeta.enableTimestamps) { updatedAt = DateTime.now().toUtc(); createdAt ??= updatedAt; } @@ -92,22 +92,22 @@ abstract class Entity { } @Target({TargetKind.classType}) -class EntityMeta { - final String table; +class Table { + final String name; final String primaryKey; - final bool timestamps; + final bool enableTimestamps; final String createdAtColumn; final String updatedAtColumn; final List? converters; - const EntityMeta({ - required this.table, + const Table({ + required this.name, this.primaryKey = 'id', - this.timestamps = false, + this.enableTimestamps = false, this.createdAtColumn = entityCreatedAtColumnName, this.updatedAtColumn = entityUpdatedAtColumnName, this.converters, @@ -115,7 +115,7 @@ class EntityMeta { } @Target({TargetKind.field}) -class EntityProperty { +class TableColumn { final String? name; - const EntityProperty({this.name}); + const TableColumn({this.name}); } diff --git a/packages/yaroorm/lib/src/reflection/reflector.dart b/packages/yaroorm/lib/src/reflection/reflector.dart index eb2f9603..86154b27 100644 --- a/packages/yaroorm/lib/src/reflection/reflector.dart +++ b/packages/yaroorm/lib/src/reflection/reflector.dart @@ -19,6 +19,7 @@ class ReflectableEntity extends r.Reflectable { r.typeCapability, r.reflectedTypeCapability, r.subtypeQuantifyCapability, + r.typeAnnotationQuantifyCapability, ); } @@ -31,38 +32,63 @@ r.ClassMirror reflectType(Type type) { } } -String getEntityTableName(Type type) => getEntityMetaData(type).table; +String getEntityTableName(Type type) => getEntityMetaData(type).name; String getEntityPrimaryKey(Type type) => getEntityMetaData(type).primaryKey; -EntityMeta getEntityMetaData(Type type) { - return reflectType(type).metadata.whereType().firstOrNull ?? - EntityMeta(table: type.toString().snakeCase.toPlural().first); +Table getEntityMetaData(Type type) { + return reflectType(type).metadata.whereType().firstOrNull ?? + Table(name: type.toString().snakeCase.toPlural().first); } class EntityPropertyData { + final bool primaryKey; + + // This is the name of property on the Dart Class final String dartName; + + // This is the name of the property in database table final String dbColumnName; + final Type type; + final bool nullable; - const EntityPropertyData(this.dartName, this.dbColumnName, this.type); + const EntityPropertyData( + this.dartName, + this.dbColumnName, + this.type, { + this.nullable = false, + this.primaryKey = false, + }); } -Map getEntityProperties(Type type, - {ClassMirror? classMirror}) { +Map getEntityProperties( + Type type, { + ClassMirror? classMirror, +}) { classMirror ??= reflectType(type); - final metadata = classMirror.metadata.whereType().firstOrNull; + final metadata = classMirror.metadata.whereType
().firstOrNull; final typeProps = classMirror.declarations.values .whereType() .fold>({}, (prev, curr) { - final propertyMeta = curr.metadata.whereType().firstOrNull; + final propertyMeta = curr.metadata.whereType().firstOrNull; + return prev - ..[curr.simpleName] = EntityPropertyData(curr.simpleName, - propertyMeta?.name ?? curr.simpleName, curr.reflectedType); + ..[curr.simpleName] = EntityPropertyData( + curr.simpleName, + propertyMeta?.name ?? curr.simpleName, + curr.reflectedType, + nullable: curr.type.isNullable, + ); }); - if (metadata == null || !metadata.timestamps) return typeProps; + + final primaryKeyProp = metadata?.primaryKey ?? 'id'; + typeProps['id'] = + EntityPropertyData('id', primaryKeyProp, int, primaryKey: true); + + if (metadata == null || !metadata.enableTimestamps) return typeProps; typeProps[metadata.createdAtColumn] ??= EntityPropertyData('createdAt', metadata.createdAtColumn, DateTime); diff --git a/packages/yaroorm/pubspec.yaml b/packages/yaroorm/pubspec.yaml index bb27598a..070e2fa8 100644 --- a/packages/yaroorm/pubspec.yaml +++ b/packages/yaroorm/pubspec.yaml @@ -15,10 +15,10 @@ environment: sdk: ^3.0.0 dependencies: - sqflite_common_ffi: ^2.3.2 - sqflite_common: ^2.3.2 + sqflite_common_ffi: + sqflite_common: ^2.5.3 mysql_client: ^0.0.27 - postgres: ^3.0.4 + postgres: ^3.1.1 meta: ^1.11.0 collection: ^1.18.0 diff --git a/packages/yaroorm/test/integration/fixtures/migrations.dart b/packages/yaroorm/test/integration/fixtures/migrations.dart index 447407ba..194633ce 100644 --- a/packages/yaroorm/test/integration/fixtures/migrations.dart +++ b/packages/yaroorm/test/integration/fixtures/migrations.dart @@ -1,25 +1,18 @@ import 'package:yaroorm/migration.dart'; +import 'package:yaroorm/src/database/driver/sqlite_driver.dart'; import 'test_data.dart'; +import 'migrations.reflectable.dart'; class AddUsersTable extends Migration { @override void up(List schemas) { - final userSchema = Schema.create('users', (table) { - return table - ..id() - ..string('firstname') - ..string('lastname') - ..integer('age') - ..string('home_address'); - }); - - schemas.add(userSchema); + schemas.add(Schema.fromEntity(User)); } @override void down(List schemas) { - schemas.add(Schema.dropIfExists('users')); + schemas.add(Schema.dropIfExists(User)); } } @@ -55,6 +48,15 @@ class AddPostsTable extends Migration { void down(List schemas) { schemas.add(Schema.dropIfExists('post_comments')); - schemas.add(Schema.dropIfExists('posts')); + schemas.add(Schema.dropIfExists(Post)); } } + +void main() { + initializeReflectable(); + + final schema = Schema.fromEntity(User); + + final script = schema.toScript(SqliteTableBlueprint()); + print(script); +} diff --git a/packages/yaroorm/test/integration/fixtures/test_data.dart b/packages/yaroorm/test/integration/fixtures/test_data.dart index 47b02b3e..fd2eb7c7 100644 --- a/packages/yaroorm/test/integration/fixtures/test_data.dart +++ b/packages/yaroorm/test/integration/fixtures/test_data.dart @@ -5,7 +5,7 @@ class User extends Entity { String lastname; int age; - @EntityProperty(name: 'home_address') + @TableColumn(name: 'home_address') String homeAddress; User({ @@ -18,7 +18,7 @@ class User extends Entity { HasMany get posts => hasMany(); } -@EntityMeta(table: 'posts', timestamps: true) +@Table(name: 'posts', enableTimestamps: true) class Post extends Entity { String title; String description; @@ -30,7 +30,7 @@ class Post extends Entity { HasMany get comments => hasMany(); } -@EntityMeta(table: 'post_comments', timestamps: true) +@Table(name: 'post_comments', enableTimestamps: true) class PostComment extends Entity { final String comment; final int? postId; diff --git a/packages/yaroorm/test/unit/dialects/sqlite_test.dart b/packages/yaroorm/test/unit/dialects/sqlite_test.dart index 0c80cdf4..59b608f4 100644 --- a/packages/yaroorm/test/unit/dialects/sqlite_test.dart +++ b/packages/yaroorm/test/unit/dialects/sqlite_test.dart @@ -7,7 +7,7 @@ import '../../integration/fixtures/orm_config.dart' as db; import '../../integration/fixtures/test_data.dart'; import 'sqlite_test.reflectable.dart'; -@EntityMeta(table: 'user_articles', primaryKey: '_id_') +@Table(name: 'user_articles', primaryKey: '_id_') class Article extends Entity { final String name; final int ownerId; diff --git a/pubspec.lock b/pubspec.lock index c8dca741..6a5ac387 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -101,10 +101,10 @@ packages: dependency: transitive description: name: http - sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.1.0" http_parser: dependency: transitive description: @@ -297,14 +297,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" - web: - dependency: transitive - description: - name: web - sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" - url: "https://pub.dev" - source: hosted - version: "0.4.2" yaml: dependency: transitive description: @@ -322,4 +314,4 @@ packages: source: hosted version: "2.1.1" sdks: - dart: ">=3.2.0 <4.0.0" + dart: ">=3.0.0 <4.0.0" From a5b27b674944f9a5a6f902fae39ae2d71d3357e8 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 3 Mar 2024 15:49:06 +0000 Subject: [PATCH 03/50] set dart sdk version in CI --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c59142de..29591f50 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -67,6 +67,8 @@ jobs: uses: actions/checkout@v3 - uses: dart-lang/setup-dart@v1.3 + with: + sdk: "3.0.0" - uses: bluefireteam/melos-action@v3 - name: Prepare Workspace From 6d2db55e3d5db10080f731f1c4f4e5a9b6adc794 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 3 Mar 2024 18:02:24 +0000 Subject: [PATCH 04/50] constrain dart sdk to 3.0.0 for yaroorm due to some issues with reflectable not working with versions higher --- packages/yaroorm/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/yaroorm/pubspec.yaml b/packages/yaroorm/pubspec.yaml index 070e2fa8..695d0108 100644 --- a/packages/yaroorm/pubspec.yaml +++ b/packages/yaroorm/pubspec.yaml @@ -12,7 +12,7 @@ topics: - sqlite environment: - sdk: ^3.0.0 + sdk: 3.0.0 dependencies: sqflite_common_ffi: From 8731b8633b1f0f6fb10a474f77525144a264b7af Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 3 Mar 2024 18:06:51 +0000 Subject: [PATCH 05/50] tiny fix --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 29591f50..c080cb53 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,6 +20,8 @@ jobs: uses: actions/checkout@v3 - uses: dart-lang/setup-dart@v1.3 + with: + sdk: "3.0.0" - uses: bluefireteam/melos-action@v3 - name: Bootstrap From f1f36b247df8e5256df5fe68a824e4abaa8f781b Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 3 Mar 2024 18:12:49 +0000 Subject: [PATCH 06/50] tiny fix --- packages/yaroorm/test/integration/fixtures/migrator.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/yaroorm/test/integration/fixtures/migrator.dart b/packages/yaroorm/test/integration/fixtures/migrator.dart index b6be5654..1a27a252 100644 --- a/packages/yaroorm/test/integration/fixtures/migrator.dart +++ b/packages/yaroorm/test/integration/fixtures/migrator.dart @@ -20,7 +20,6 @@ Future runMigrator(String connectionName, String command) async { print('> dart ${commands.join(' ')}\n'); final result = await Process.run('dart', commands); - stdout.write(result.stdout); stderr.write("""-------------------------------------------- \n${result.stderr}\n --------------------------------------------"""); From e01219576d621bab111c8828860f54c2ab9e7fe4 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Tue, 5 Mar 2024 23:50:32 +0000 Subject: [PATCH 07/50] tiny fix --- packages/yaroorm/test/integration/fixtures/migrator.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/yaroorm/test/integration/fixtures/migrator.dart b/packages/yaroorm/test/integration/fixtures/migrator.dart index 1a27a252..391d174d 100644 --- a/packages/yaroorm/test/integration/fixtures/migrator.dart +++ b/packages/yaroorm/test/integration/fixtures/migrator.dart @@ -20,9 +20,7 @@ Future runMigrator(String connectionName, String command) async { print('> dart ${commands.join(' ')}\n'); final result = await Process.run('dart', commands); - stderr.write("""-------------------------------------------- - \n${result.stderr}\n - --------------------------------------------"""); + stderr.write(result.stderr); expect(result.exitCode, 0); } From 2487484f0a4ec17fcecd2ab76a4464c1dbb840ca Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Wed, 6 Mar 2024 16:23:27 +0000 Subject: [PATCH 08/50] tiny fix --- packages/yaroorm/pubspec.yaml | 2 +- packages/yaroorm/test/integration/fixtures/migrations.dart | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/yaroorm/pubspec.yaml b/packages/yaroorm/pubspec.yaml index 695d0108..070e2fa8 100644 --- a/packages/yaroorm/pubspec.yaml +++ b/packages/yaroorm/pubspec.yaml @@ -12,7 +12,7 @@ topics: - sqlite environment: - sdk: 3.0.0 + sdk: ^3.0.0 dependencies: sqflite_common_ffi: diff --git a/packages/yaroorm/test/integration/fixtures/migrations.dart b/packages/yaroorm/test/integration/fixtures/migrations.dart index 194633ce..74402d7d 100644 --- a/packages/yaroorm/test/integration/fixtures/migrations.dart +++ b/packages/yaroorm/test/integration/fixtures/migrations.dart @@ -55,8 +55,8 @@ class AddPostsTable extends Migration { void main() { initializeReflectable(); - final schema = Schema.fromEntity(User); + // final schema = Schema.fromEntity(User); - final script = schema.toScript(SqliteTableBlueprint()); - print(script); + // final script = schema.toScript(SqliteTableBlueprint()); + // print(script); } From 34135662e181f03b678cca9f6caaa7de24a4912f Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sat, 23 Mar 2024 20:16:08 +0000 Subject: [PATCH 09/50] remove reflectable from yaroorm --- packages/yaroorm/lib/migration.dart | 100 ++++++++------ .../yaroorm/lib/src/database/database.dart | 7 +- .../src/database/driver/sqlite_driver.dart | 25 ++-- .../lib/src/database/entity/converter.dart | 71 +++------- .../lib/src/database/entity/entity.dart | 123 +++++------------ .../lib/src/database/entity/relations.dart | 37 ++--- .../yaroorm/lib/src/primitives/where.dart | 3 +- packages/yaroorm/lib/src/query/query.dart | 32 +++-- .../yaroorm/lib/src/query/query_impl.dart | 6 +- .../yaroorm/lib/src/reflection/reflector.dart | 128 +++++++----------- packages/yaroorm/lib/src/reflection/util.dart | 23 ---- packages/yaroorm/pubspec.yaml | 5 +- .../test/integration/fixtures/migrations.dart | 9 -- 13 files changed, 228 insertions(+), 341 deletions(-) delete mode 100644 packages/yaroorm/lib/src/reflection/util.dart diff --git a/packages/yaroorm/lib/migration.dart b/packages/yaroorm/lib/migration.dart index 4c8fa36c..afdf89ee 100644 --- a/packages/yaroorm/lib/migration.dart +++ b/packages/yaroorm/lib/migration.dart @@ -7,20 +7,7 @@ import 'package:yaroorm/yaroorm.dart'; abstract class TableBlueprint { void id({String name = 'id', String? type, bool autoIncrement = true}); - void foreign({ - String? column, - ForeignKey Function(ForeignKey fkey)? onKey, - }) { - final table = getEntityTableName(Model); - final colName = column ?? '${ReferenceModel.toString().camelCase}Id'; - - final referenceTable = getEntityTableName(ReferenceModel); - final referenceTablePrimaryKey = getEntityPrimaryKey(ReferenceModel); - final fkey = ForeignKey(table, colName, - foreignTable: referenceTable, - foreignTableColumn: referenceTablePrimaryKey); - onKey?.call(fkey); - } + void foreign(ForeignKey key); void string(String name, {bool nullable = false, String? defaultValue}); @@ -37,8 +24,8 @@ abstract class TableBlueprint { void blob(String name, {bool nullable = false, String? defaultValue}); void timestamps({ - String createdAt = entityCreatedAtColumnName, - String updatedAt = entityUpdatedAtColumnName, + String createdAt = 'createdAt', + String updatedAt = 'updatedAt', }); /// NUMBER TYPES @@ -172,45 +159,46 @@ abstract class TableBlueprint { typedef TableBluePrintFunc = TableBlueprint Function(TableBlueprint table); -class Schema { +abstract class Schema { final String tableName; final TableBluePrintFunc? _bluePrintFunc; Schema._(this.tableName, this._bluePrintFunc); - String toScript(TableBlueprint table) => - _bluePrintFunc!.call(table).createScript(tableName); + String toScript(TableBlueprint table); - static Schema fromEntity(Type entity) { - make(TableBlueprint table, EntityPropertyData data) { - return switch (data.type) { + static CreateSchema fromEntity() { + final entity = Query.getEntity(); + + make(TableBlueprint table, DBEntityField field) { + return switch (field.type) { const (int) => table - ..integer(data.dbColumnName, nullable: data.nullable), + ..integer(field.columnName, nullable: field.nullable), const (double) || const (num) => table - ..double(data.dbColumnName, nullable: data.nullable), + ..double(field.columnName, nullable: field.nullable), const (DateTime) => table - ..datetime(data.dbColumnName, nullable: data.nullable), - _ => table..string(data.dbColumnName, nullable: data.nullable), + ..datetime(field.columnName, nullable: field.nullable), + _ => table..string(field.columnName, nullable: field.nullable), }; } - final props = getEntityProperties(entity); - final primaryKey = props.values.firstWhere((e) => e.primaryKey); - - return Schema.create(getEntityTableName(entity), (table) { - table.id(name: primaryKey.dbColumnName); - for (final prop in props.values.where((e) => !e.primaryKey)) { - make(table, prop); - } - return table; - }); + return CreateSchema._( + entity.tableName, + (table) { + table.id(name: entity.primaryKey.columnName); + for (final prop in entity.columns.where((e) => !e.primaryKey)) { + make(table, prop); + } + return table; + }, + ); } static Schema create(String name, TableBluePrintFunc func) => - Schema._(name, func); + CreateSchema._(name, func); static Schema dropIfExists(dynamic value) { - if (value is! String) value = getEntityTableName(value); + // if (value is! String) value = getEntityTableName(value); return _DropSchema(value); } @@ -229,6 +217,42 @@ abstract class Migration { void down(List schemas); } +final class CreateSchema extends Schema { + final List _foreignKeys; + + CreateSchema._(super.name, super.func) + : _foreignKeys = [], + super._(); + + @override + String toScript(TableBlueprint table) { + table = _bluePrintFunc!.call(table); + for (final key in _foreignKeys) { + table.foreign(key); + } + return table.createScript(tableName); + } + + void foreign({ + String? column, + ForeignKey Function(ForeignKey fkey)? onKey, + }) { + final referenceColumn = + column ?? '${ReferenceModel.toString().camelCase}Id'; + + final referenceTable = getEntityTableName(); + final referenceTablePrimaryKey = getEntityPrimaryKey(); + final fkey = ForeignKey( + tableName, + referenceColumn, + foreignTable: referenceTable, + foreignTableColumn: referenceTablePrimaryKey, + ); + onKey?.call(fkey); + _foreignKeys.add(fkey); + } +} + class _DropSchema extends Schema { _DropSchema(String name) : super._(name, null); diff --git a/packages/yaroorm/lib/src/database/database.dart b/packages/yaroorm/lib/src/database/database.dart index 16b4d955..b8c16010 100644 --- a/packages/yaroorm/lib/src/database/database.dart +++ b/packages/yaroorm/lib/src/database/database.dart @@ -9,8 +9,9 @@ class UseDatabaseConnection { UseDatabaseConnection(this.info) : driver = DB.driver(info.name); - Query query([String? table]) => - Query.table(table).driver(driver)..database = info.database; + Query query([String? table]) { + return Query.table(table).driver(driver)..database = info.database; + } } const String pleaseInitializeMessage = 'DB has not been initialized.\n' @@ -27,7 +28,7 @@ class DB { static DatabaseDriver get defaultDriver => defaultConnection.driver; - static Query query([String? table]) => + static Query query([String? table]) => defaultConnection.query(table); static UseDatabaseConnection connection(String connName) => diff --git a/packages/yaroorm/lib/src/database/driver/sqlite_driver.dart b/packages/yaroorm/lib/src/database/driver/sqlite_driver.dart index 34d7dced..091978e1 100644 --- a/packages/yaroorm/lib/src/database/driver/sqlite_driver.dart +++ b/packages/yaroorm/lib/src/database/driver/sqlite_driver.dart @@ -26,8 +26,10 @@ final class SqliteDriver implements DatabaseDriver { SqliteDriver(this.config); @override - Future connect( - {int? maxConnections, bool? singleConnection}) async { + Future connect({ + int? maxConnections, + bool? singleConnection, + }) async { assert(maxConnections == null, 'Sqlite does not support max connections'); assert( singleConnection == null, 'Sqlite does not support single connection'); @@ -529,9 +531,10 @@ class SqliteTableBlueprint extends TableBlueprint { } @override - void timestamps( - {String createdAt = entityCreatedAtColumnName, - String updatedAt = entityUpdatedAtColumnName}) { + void timestamps({ + String createdAt = 'createdAt', + String updatedAt = 'updatedAt', + }) { timestamp(createdAt); timestamp(updatedAt); } @@ -710,15 +713,7 @@ class SqliteTableBlueprint extends TableBlueprint { } @override - void foreign({ - String? column, - ForeignKey Function(ForeignKey fkey)? onKey, - }) { - late ForeignKey result; - callback(ForeignKey fkey) => result = onKey?.call(fkey) ?? fkey; - - super.foreign(column: column, onKey: callback); - final statement = szler.acceptForeignKey(this, result); - _foreignKeys.add(statement); + void foreign(ForeignKey key) { + _foreignKeys.add(szler.acceptForeignKey(this, key)); } } diff --git a/packages/yaroorm/lib/src/database/entity/converter.dart b/packages/yaroorm/lib/src/database/entity/converter.dart index cf3debb2..259e2fdd 100644 --- a/packages/yaroorm/lib/src/database/entity/converter.dart +++ b/packages/yaroorm/lib/src/database/entity/converter.dart @@ -52,74 +52,41 @@ Map _combineConverters( }; } -Map _serializeEntityProps( - T instance, { +Map _serializeEntityProps( + Model instance, { List converters = const [], }) { - final entityMeta = getEntityMetaData(instance.runtimeType); - final entityProperties = getEntityProperties(instance.runtimeType); - if (instance.id != null) { - entityProperties['id'] = EntityPropertyData( - 'id', entityMeta.primaryKey, instance.id.runtimeType); - } + final entity = Query.getEntity(); - final instanceMirror = entity.reflect(instance); - final mappedConverters = - _combineConverters(entityMeta.converters ?? [], converters); + final instanceMirror = entity.mirror(instance); + final allConverters = _combineConverters(entity.converters, converters); /// database value conversion back to Dart Types - toDartValue(MapEntry entry) { - final value = instanceMirror.invokeGetter(entry.key); - final typeConverter = mappedConverters[entry.value.type]; + toDartValue(DBEntityField field) { + final value = instanceMirror.get(field.dartName); + final typeConverter = allConverters[field.type]; return typeConverter == null ? value : typeConverter.toDbType(value); } return { - for (final entry in entityProperties.entries) - entry.value.dbColumnName: toDartValue(entry), + for (final entry in entity.columns) entry.columnName: toDartValue(entry), }; } -Entity serializedPropsToEntity( +Entity serializedPropsToEntity( final Map json, { List converters = const [], }) { - final mirror = reflectEntity(); - final entityMeta = getEntityMetaData(Model); - final entityProperties = getEntityProperties(Model); - final constructorMethod = mirror.declarations.entries - .firstWhereOrNull((e) => e.key == '$Model') - ?.value as MethodMirror; - final constructorParams = constructorMethod.parameters; - - final mappedConverters = - _combineConverters(entityMeta.converters ?? [], converters); - - /// conversion to Database compatible types using [EntityTypeConverter] - final transformedRecordMap = {}; - for (final entry in entityProperties.entries) { - final value = json[entry.value.dbColumnName]; - final typeConverter = mappedConverters[entry.value.type]; - transformedRecordMap[entry.value.dartName] = + final entity = Query.getEntity(); + final allConverters = _combineConverters(entity.converters, converters); + + final resultsMap = {}; + for (final entry in entity.columns) { + final value = json[entry.columnName]; + final typeConverter = allConverters[entry.type]; + resultsMap[entry.dartName] = typeConverter == null ? value : typeConverter.fromDbType(value); } - final namedDeps = constructorParams - .where((e) => e.isNamed) - .map((e) => - (name: e.simpleName, value: transformedRecordMap[e.simpleName])) - .fold>( - {}, (prev, e) => prev..[Symbol(e.name)] = e.value); - - final dependencies = constructorParams - .where((e) => !e.isNamed) - .map((e) => transformedRecordMap[e.simpleName]) - .toList(); - - final newEntityInstance = mirror.newInstance('', dependencies, namedDeps); - return (newEntityInstance as Entity) - ..id = json[entityMeta.primaryKey] - ..createdAt = transformedRecordMap[entityMeta.createdAtColumn] - ..updatedAt = transformedRecordMap[entityMeta.updatedAtColumn] - .._isLoadedFromDB = true; + return entity.build(resultsMap); } diff --git a/packages/yaroorm/lib/src/database/entity/entity.dart b/packages/yaroorm/lib/src/database/entity/entity.dart index ca69eaf5..275b7b94 100644 --- a/packages/yaroorm/lib/src/database/entity/entity.dart +++ b/packages/yaroorm/lib/src/database/entity/entity.dart @@ -1,115 +1,45 @@ -import 'package:collection/collection.dart'; import 'package:recase/recase.dart'; -import 'package:reflectable/reflectable.dart'; import 'package:yaroorm/yaroorm.dart'; import 'package:meta/meta_meta.dart'; -import '../../primitives/where.dart'; -import '../../reflection/util.dart'; - part 'converter.dart'; part 'relations.dart'; -const entity = ReflectableEntity(); - -const String entityCreatedAtColumnName = 'createdAt'; -const String entityUpdatedAtColumnName = 'updatedAt'; - -@entity -abstract class Entity { - Entity() { - assert(runtimeType == Model, - 'Type Mismatch on Entity<$PkType, $Model>. $runtimeType expected'); - if (PkType == dynamic) { - throw Exception( - 'Entity Primary Key Data Type is required. Use either `extends Entity` or `Entity`'); - } +abstract class Entity { + String get _foreignKeyForModel => '${Model.toString().camelCase}Id'; - if (connection != null) _driver = DB.driver(connection!); + HasOne hasOne({ + String? foreignKey, + }) { + return HasOne(foreignKey ?? _foreignKeyForModel, this); } - PkType? id; - - DateTime? createdAt; - - DateTime? updatedAt; - - Table? _entityMetaCache; - - Table get entityMeta => _entityMetaCache ??= getEntityMetaData(Model); - - DriverContract _driver = DB.defaultDriver; - - String? get connection => null; - - Model withDriver(DriverContract driver) { - _driver = driver; - return this as Model; + HasMany hasMany({ + String? foreignKey, + }) { + return HasMany(foreignKey ?? _foreignKeyForModel, this); } - Query get query => DB.query().driver(_driver); - - WhereClause _whereId(Query q) => - q.whereEqual(entityMeta.primaryKey, id); - - bool _isLoadedFromDB = false; + final DriverContract _driver = DB.defaultDriver; - Future delete() => query.delete(_whereId).execute(); - - Future save() async { - if (_isLoadedFromDB) { - assert(id != null, 'Id cannot be null when loaded from database'); - if (entityMeta.enableTimestamps) updatedAt = DateTime.now().toUtc(); - await query.update(where: _whereId, values: to_db_data).execute(); - return this as Model; - } - - final recordId = await query.insert(to_db_data); - return (this - ..id = recordId - .._isLoadedFromDB = true) as Model; + Entity withDriver(DriverContract driver) { + return this; } // ignore: non_constant_identifier_names Map get to_db_data { - if (entityMeta.enableTimestamps) { - updatedAt = DateTime.now().toUtc(); - createdAt ??= updatedAt; - } return _serializeEntityProps(this, converters: _driver.typeconverters); } - - String get _foreignKeyForModel => '${Model.toString().camelCase}Id'; - - HasOne hasOne( - {String? foreignKey}) => - HasOne(foreignKey ?? _foreignKeyForModel, this); - - HasMany hasMany( - {String? foreignKey}) => - HasMany(foreignKey ?? _foreignKeyForModel, this); } @Target({TargetKind.classType}) class Table { final String name; - - final String primaryKey; - - final bool enableTimestamps; - - final String createdAtColumn; - final String updatedAtColumn; - final List? converters; - const Table({ - required this.name, - this.primaryKey = 'id', - this.enableTimestamps = false, - this.createdAtColumn = entityCreatedAtColumnName, - this.updatedAtColumn = entityUpdatedAtColumnName, + const Table( + this.name, { this.converters, }); } @@ -117,5 +47,26 @@ class Table { @Target({TargetKind.field}) class TableColumn { final String? name; - const TableColumn({this.name}); + final bool nullable; + + const TableColumn({this.name, this.nullable = false}); } + +@Target({TargetKind.field}) +class PrimaryKey extends TableColumn { + const PrimaryKey(); +} + +@Target({TargetKind.field}) +class CreatedAtColumn extends TableColumn { + const CreatedAtColumn() : super(name: 'createdAt', nullable: false); +} + +@Target({TargetKind.field}) +class UpdatedAtColumn extends TableColumn { + const UpdatedAtColumn() : super(name: 'updatedAt', nullable: false); +} + +const primaryKey = PrimaryKey(); +const createdAtCol = CreatedAtColumn(); +const updatedAtCol = UpdatedAtColumn(); diff --git a/packages/yaroorm/lib/src/database/entity/relations.dart b/packages/yaroorm/lib/src/database/entity/relations.dart index 9992168a..5cec67ae 100644 --- a/packages/yaroorm/lib/src/database/entity/relations.dart +++ b/packages/yaroorm/lib/src/database/entity/relations.dart @@ -22,6 +22,11 @@ abstract class EntityRelation { EntityRelation(this.owner) : _query = Query.table().driver(owner._driver); + Object get ownerId { + final typeData = Query.getEntity(type: owner.runtimeType); + return typeData.mirror.call(owner).get(typeData.primaryKey.dartName)!; + } + DriverContract get _driver => owner._driver; get(); @@ -36,22 +41,21 @@ class HasOne extends EntityRelation HasOne(this.foreignKey, super._owner); Future set(RelatedModel model) async { - model.withDriver(_driver); - - final data = model.to_db_data..[foreignKey] = owner.id!; - data[model.entityMeta.primaryKey] = await _query.insert(data); - return serializedPropsToEntity( - data, - converters: _driver.typeconverters, - ).withDriver(_driver) as RelatedModel; + throw Exception(); + // final data = model.to_db_data..[foreignKey] = ownerId; + // data[model.entityMeta.primaryKey] = await _query.insert(data); + // return serializedPropsToEntity( + // data, + // converters: _driver.typeconverters, + // ).withDriver(_driver) as RelatedModel; } @override Future get() => - _query.whereEqual(foreignKey, owner.id!).findOne(); + _query.whereEqual(foreignKey, ownerId).findOne(); @override - Future delete() => _query.whereEqual(foreignKey, owner.id!).delete(); + Future delete() => _query.whereEqual(foreignKey, ownerId).delete(); } class HasMany extends EntityRelation @@ -61,15 +65,16 @@ class HasMany extends EntityRelation HasMany(this.foreignKey, super.owner); @override - Future> get() => - _query.whereEqual(foreignKey, owner.id!).findMany(); + Future> get() { + return _query.whereEqual(foreignKey, ownerId).findMany(); + } Future first() => - _query.whereEqual(foreignKey, owner.id!).findOne(); + _query.whereEqual(foreignKey, ownerId).findOne(); Map _serializeModel(RelatedModel model) { - model.withDriver(_driver); - return model.to_db_data..[foreignKey] = owner.id!; + // model.withDriver(_driver); + return model.to_db_data..[foreignKey] = ownerId; } Future add(RelatedModel model) => _query.insert(_serializeModel(model)); @@ -78,5 +83,5 @@ class HasMany extends EntityRelation _query.insertMany(model.map(_serializeModel).toList()); @override - Future delete() => _query.whereEqual(foreignKey, owner.id!).delete(); + Future delete() => _query.whereEqual(foreignKey, ownerId).delete(); } diff --git a/packages/yaroorm/lib/src/primitives/where.dart b/packages/yaroorm/lib/src/primitives/where.dart index bfceb258..35158b17 100644 --- a/packages/yaroorm/lib/src/primitives/where.dart +++ b/packages/yaroorm/lib/src/primitives/where.dart @@ -1,6 +1,7 @@ // ignore_for_file: constant_identifier_names import 'package:yaroorm/src/query/aggregates.dart'; +import 'package:yaroorm/yaroorm.dart'; import '../query/query.dart'; @@ -157,8 +158,6 @@ abstract class WhereClause ..clauseValue = value; } - Future update(Map values); - Future delete(); String get statement; diff --git a/packages/yaroorm/lib/src/query/query.dart b/packages/yaroorm/lib/src/query/query.dart index 8ce768dd..6e92a6b2 100644 --- a/packages/yaroorm/lib/src/query/query.dart +++ b/packages/yaroorm/lib/src/query/query.dart @@ -1,10 +1,8 @@ import 'package:meta/meta.dart'; import 'package:yaroorm/src/query/aggregates.dart'; +import 'package:yaroorm/yaroorm.dart'; -import '../database/driver/driver.dart'; -import '../database/entity/entity.dart'; import '../primitives/where.dart'; -import '../reflection/reflector.dart'; part 'query_impl.dart'; @@ -33,9 +31,10 @@ mixin LimitOperation { } mixin UpdateOperation { - UpdateQuery update( - {required WhereClause Function(Query query) where, - required Map values}); + UpdateQuery update({ + required WhereClause Function(Query query) where, + required Map values, + }); } mixin DeleteOperation { @@ -91,6 +90,8 @@ abstract interface class Query extends QueryBase> final Set orderByProps; final List> whereClauses; + static final Map _typedata = {}; + // ignore: prefer_final_fields int? _limit; @@ -102,9 +103,9 @@ abstract interface class Query extends QueryBase> whereClauses = [], _limit = null; - static Query table([String? tableName]) { + static Query table([String? tableName]) { if (Model != Entity && Model != dynamic) { - tableName ??= getEntityTableName(Model); + tableName ??= getEntityTableName(); } assert(tableName != null, 'Either provide Entity Type or tableName'); return QueryImpl(tableName!); @@ -136,6 +137,21 @@ abstract interface class Query extends QueryBase> data: values, ).driver(runner); } + + static void addTypeDef(DBEntity entity) { + var type = T; + if (type == Entity) type = entity.dartType; + if (type == Entity) throw Exception(); + _typedata[type] = entity; + } + + @internal + static DBEntity getEntity({Type? type}) { + type ??= T; + return _typedata.containsKey(type) + ? _typedata[type]! + : throw Exception('Type Data not found for $type'); + } } mixin AggregateOperation { diff --git a/packages/yaroorm/lib/src/query/query_impl.dart b/packages/yaroorm/lib/src/query/query_impl.dart index d059e605..43dfe0e2 100644 --- a/packages/yaroorm/lib/src/query/query_impl.dart +++ b/packages/yaroorm/lib/src/query/query_impl.dart @@ -2,7 +2,7 @@ part of 'query.dart'; enum OrderByDirection { asc, desc } -class QueryImpl extends Query { +class QueryImpl extends Query { QueryImpl(super.tableName); @override @@ -57,13 +57,13 @@ class QueryImpl extends Query { @override Future get([dynamic id]) async { if (id != null) { - return whereEqual(getEntityPrimaryKey(Result), id).findOne(); + return whereEqual(getEntityPrimaryKey(), id).findOne(); } return (await take(1)).firstOrNull; } /// [T] is the expected type passed to [Query] via Query - T _wrapRawResult(Map? result) { + T _wrapRawResult(Map? result) { if (T == dynamic || result == null) return result as dynamic; return (serializedPropsToEntity( result, diff --git a/packages/yaroorm/lib/src/reflection/reflector.dart b/packages/yaroorm/lib/src/reflection/reflector.dart index 86154b27..c64b013a 100644 --- a/packages/yaroorm/lib/src/reflection/reflector.dart +++ b/packages/yaroorm/lib/src/reflection/reflector.dart @@ -1,100 +1,64 @@ -import 'package:collection/collection.dart'; -import 'package:grammer/grammer.dart'; -import 'package:recase/recase.dart'; -import 'package:reflectable/mirrors.dart'; -import 'package:reflectable/reflectable.dart' as r; - -import '../database/entity/entity.dart'; -import 'util.dart'; - -class ReflectableEntity extends r.Reflectable { - const ReflectableEntity() - : super( - r.declarationsCapability, - r.metadataCapability, - r.invokingCapability, - r.newInstanceCapability, - - /// - r.typeCapability, - r.reflectedTypeCapability, - r.subtypeQuantifyCapability, - r.typeAnnotationQuantifyCapability, - ); -} +import 'package:yaroorm/yaroorm.dart'; -r.ClassMirror reflectType(Type type) { - try { - return entity.reflectType(type) as r.ClassMirror; - } catch (e) { - throw EntityValidationException( - 'Unable to reflect on $type. Re-run your build command'); - } -} +String getEntityTableName() => Query.getEntity().tableName; + +String getEntityPrimaryKey() => + Query.getEntity().primaryKey.columnName; -String getEntityTableName(Type type) => getEntityMetaData(type).name; +typedef EntityInstanceReflector = EntityMirror Function(T instance); -String getEntityPrimaryKey(Type type) => getEntityMetaData(type).primaryKey; +typedef EntityInstanceBuilder = T Function(Map args); -Table getEntityMetaData(Type type) { - return reflectType(type).metadata.whereType
().firstOrNull ?? - Table(name: type.toString().snakeCase.toPlural().first); +abstract class EntityMirror { + final T instance; + const EntityMirror(this.instance); + + Object? get(Symbol field); } -class EntityPropertyData { - final bool primaryKey; +class DBEntity { + Type get dartType => T; - // This is the name of property on the Dart Class - final String dartName; + final String tableName; + final List columns; - // This is the name of the property in database table - final String dbColumnName; + final EntityInstanceReflector mirror; + final EntityInstanceBuilder build; - final Type type; - final bool nullable; + final List converters; - const EntityPropertyData( - this.dartName, - this.dbColumnName, - this.type, { - this.nullable = false, - this.primaryKey = false, - }); -} + final bool timestampsEnabled; + + DBEntityField get primaryKey => columns.firstWhere((e) => e.primaryKey); -Map getEntityProperties( - Type type, { - ClassMirror? classMirror, -}) { - classMirror ??= reflectType(type); - - final metadata = classMirror.metadata.whereType
().firstOrNull; - - final typeProps = classMirror.declarations.values - .whereType() - .fold>({}, (prev, curr) { - final propertyMeta = curr.metadata.whereType().firstOrNull; - - return prev - ..[curr.simpleName] = EntityPropertyData( - curr.simpleName, - propertyMeta?.name ?? curr.simpleName, - curr.reflectedType, - nullable: curr.type.isNullable, - ); + const DBEntity( + this.tableName, { + this.columns = const [], + required this.mirror, + required this.build, + this.timestampsEnabled = false, + this.converters = const [], }); +} - final primaryKeyProp = metadata?.primaryKey ?? 'id'; - typeProps['id'] = - EntityPropertyData('id', primaryKeyProp, int, primaryKey: true); +class DBEntityField { + /// dart name for property on Entity class + final Symbol dartName; - if (metadata == null || !metadata.enableTimestamps) return typeProps; + /// Column name in the database + final String columnName; - typeProps[metadata.createdAtColumn] ??= - EntityPropertyData('createdAt', metadata.createdAtColumn, DateTime); + /// Dart primitive type + final Type type; - typeProps[metadata.updatedAtColumn] ??= - EntityPropertyData('updatedAt', metadata.updatedAtColumn, DateTime); + final bool nullable; + final bool primaryKey; - return typeProps; + const DBEntityField( + this.columnName, + this.type, + this.dartName, { + required this.nullable, + this.primaryKey = false, + }); } diff --git a/packages/yaroorm/lib/src/reflection/util.dart b/packages/yaroorm/lib/src/reflection/util.dart deleted file mode 100644 index 4f72c1ef..00000000 --- a/packages/yaroorm/lib/src/reflection/util.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:reflectable/reflectable.dart'; -import 'reflector.dart'; - -class EntityValidationException implements Exception { - final String message; - - EntityValidationException(this.message); - - @override - String toString() => 'EntityValidationException: $message'; -} - -ClassMirror reflectEntity() { - late ClassMirror mirror; - - try { - mirror = (reflectType(Model)); - } catch (e) { - throw EntityValidationException( - "Either $Model is not a subtype of Entity or re-run your build_runner command"); - } - return mirror; -} diff --git a/packages/yaroorm/pubspec.yaml b/packages/yaroorm/pubspec.yaml index 070e2fa8..1b13568e 100644 --- a/packages/yaroorm/pubspec.yaml +++ b/packages/yaroorm/pubspec.yaml @@ -24,13 +24,10 @@ dependencies: collection: ^1.18.0 grammer: ^1.0.3 recase: ^4.1.0 - reflectable: ^4.0.5 dev_dependencies: lints: ^3.0.0 test: ^1.24.0 yaroo_cli: - git: - url: https://github.com/codekeyz/yaroo.git - path: packages/yaroo_cli + path: ../yaroo_cli path: ^1.9.0 diff --git a/packages/yaroorm/test/integration/fixtures/migrations.dart b/packages/yaroorm/test/integration/fixtures/migrations.dart index 74402d7d..7dd8449e 100644 --- a/packages/yaroorm/test/integration/fixtures/migrations.dart +++ b/packages/yaroorm/test/integration/fixtures/migrations.dart @@ -51,12 +51,3 @@ class AddPostsTable extends Migration { schemas.add(Schema.dropIfExists(Post)); } } - -void main() { - initializeReflectable(); - - // final schema = Schema.fromEntity(User); - - // final script = schema.toScript(SqliteTableBlueprint()); - // print(script); -} From 5234c5053a283fe6974ada33ec974d71bceb099d Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 5 Apr 2024 23:31:41 +0000 Subject: [PATCH 10/50] rework orm cli --- packages/yaroo_cli/lib/orm/_misc.dart | 13 +-- .../lib/orm/commands/migrate_command.dart | 7 +- .../orm/commands/migrate_reset_command.dart | 8 +- .../commands/migrate_rollback_command.dart | 10 +-- packages/yaroo_cli/lib/orm/orm.dart | 7 -- packages/yaroo_cli/lib/src/migration.dart | 80 +++++++++++++++++++ 6 files changed, 93 insertions(+), 32 deletions(-) create mode 100644 packages/yaroo_cli/lib/src/migration.dart diff --git a/packages/yaroo_cli/lib/orm/_misc.dart b/packages/yaroo_cli/lib/orm/_misc.dart index d7ff4537..62b43f4b 100644 --- a/packages/yaroo_cli/lib/orm/_misc.dart +++ b/packages/yaroo_cli/lib/orm/_misc.dart @@ -1,18 +1,11 @@ -import 'package:yaroorm/migration.dart'; +import 'package:yaroo_cli/src/migration.dart'; import 'package:yaroorm/yaroorm.dart'; -final _migrationsSchema = Schema.create('migrations', ($table) { - return $table - ..id() - ..string('migration') - ..integer('batch'); -}); - Future ensureMigrationsTableReady(DatabaseDriver driver) async { final hasTable = await driver.hasTable(DB.config.migrationsTable); if (hasTable) return; - final script = _migrationsSchema.toScript(driver.blueprint); + final script = MigrationDataSchema.toScript(driver.blueprint); await driver.execute(script); } @@ -22,7 +15,7 @@ Future hasAlreadyMigratedScript( ) async { final result = await Query.table(DB.config.migrationsTable) .driver(driver) - .whereEqual('migration', scriptName) + .equal('migration', scriptName) .findOne(); return result != null; } diff --git a/packages/yaroo_cli/lib/orm/commands/migrate_command.dart b/packages/yaroo_cli/lib/orm/commands/migrate_command.dart index b744fdda..d7c5e913 100644 --- a/packages/yaroo_cli/lib/orm/commands/migrate_command.dart +++ b/packages/yaroo_cli/lib/orm/commands/migrate_command.dart @@ -1,10 +1,10 @@ import 'dart:async'; import 'package:mason_logger/mason_logger.dart'; +import 'package:yaroo_cli/src/migration.dart'; import 'package:yaroorm/yaroorm.dart'; import '../../src/logger.dart'; import '../_misc.dart'; -import '../orm.dart'; import 'command.dart'; class MigrateCommand extends OrmCommand { @@ -39,9 +39,8 @@ class MigrateCommand extends OrmCommand { await txnDriver.execute(sql); } - await Query.table(migrationTableName) - .driver(txnDriver) - .insert(MigrationData(fileName, batchNos).to_db_data); + await MigrationQuery.driver(driver) + .create(migration: fileName, batch: batchNos); print('✔ done: $fileName'); }); diff --git a/packages/yaroo_cli/lib/orm/commands/migrate_reset_command.dart b/packages/yaroo_cli/lib/orm/commands/migrate_reset_command.dart index 46c3814a..ff4c1c05 100644 --- a/packages/yaroo_cli/lib/orm/commands/migrate_reset_command.dart +++ b/packages/yaroo_cli/lib/orm/commands/migrate_reset_command.dart @@ -1,10 +1,10 @@ import 'dart:async'; import 'package:collection/collection.dart'; +import 'package:yaroo_cli/src/migration.dart'; import 'package:yaroorm/yaroorm.dart'; import '../_misc.dart'; -import '../orm.dart'; import 'command.dart'; import 'migrate_rollback_command.dart'; @@ -19,10 +19,8 @@ class MigrationResetCommand extends OrmCommand { Future execute(DatabaseDriver driver) async { await ensureMigrationsTableReady(driver); - final migrationsList = await Query.table(migrationTableName) - .driver(driver) - .orderByDesc('batch') - .all(); + final migrationsList = + await MigrationQuery.driver(driver).orderByDesc('batch').all(); if (migrationsList.isEmpty) { print('𐄂 skipped: reason: no migrations to reset'); return; diff --git a/packages/yaroo_cli/lib/orm/commands/migrate_rollback_command.dart b/packages/yaroo_cli/lib/orm/commands/migrate_rollback_command.dart index a43373a4..45a816a3 100644 --- a/packages/yaroo_cli/lib/orm/commands/migrate_rollback_command.dart +++ b/packages/yaroo_cli/lib/orm/commands/migrate_rollback_command.dart @@ -1,11 +1,11 @@ import 'dart:async'; import 'package:collection/collection.dart'; +import 'package:yaroo_cli/src/migration.dart'; import 'package:yaroorm/migration.dart'; import 'package:yaroorm/yaroorm.dart'; import '../_misc.dart'; -import '../orm.dart'; import 'command.dart'; class MigrationRollbackCommand extends OrmCommand { @@ -22,10 +22,8 @@ class MigrationRollbackCommand extends OrmCommand { final lastBatchNumber = await getLastBatchNumber(driver, migrationTableName); - final entries = await DB - .connection(dbConnection) - .query(migrationTableName) - .whereEqual('batch', lastBatchNumber) + final entries = await MigrationQuery.driver(driver) + .equal('batch', lastBatchNumber) .findMany(); /// rollbacks start from the last class listed in the migrations list @@ -66,7 +64,7 @@ Future processRollbacks( await Query.table( DB.config.migrationsTable, - ).driver(transactor).whereEqual('id', rollback.entry.id!).delete(); + ).driver(transactor).equal('id', rollback.entry.id).delete(); }); print('✔ rolled back: ${rollback.entry.migration}'); diff --git a/packages/yaroo_cli/lib/orm/orm.dart b/packages/yaroo_cli/lib/orm/orm.dart index 95b02b0a..72763969 100644 --- a/packages/yaroo_cli/lib/orm/orm.dart +++ b/packages/yaroo_cli/lib/orm/orm.dart @@ -9,13 +9,6 @@ import 'commands/migrate_command.dart'; import 'commands/migrate_reset_command.dart'; import 'commands/migrate_rollback_command.dart'; -class MigrationData extends Entity { - final String migration; - final int batch; - - MigrationData(this.migration, this.batch); -} - const executableName = 'yaroo orm'; const packageName = 'yaroo_cli'; const description = 'yaroorm command-line tool'; diff --git a/packages/yaroo_cli/lib/src/migration.dart b/packages/yaroo_cli/lib/src/migration.dart new file mode 100644 index 00000000..39bbe9f6 --- /dev/null +++ b/packages/yaroo_cli/lib/src/migration.dart @@ -0,0 +1,80 @@ +// ignore_for_file: non_constant_identifier_names + +import 'package:yaroorm/migration.dart'; +import 'package:yaroorm/yaroorm.dart'; + +class MigrationData extends Entity { + @primaryKey + final int id; + + final String migration; + + final int batch; + + MigrationData(this.id, this.migration, this.batch); +} + +class _$MigrationDataEntityMirror extends EntityMirror { + const _$MigrationDataEntityMirror(super.instance); + + @override + Object? get(Symbol field) => switch (field) { + #id => instance.id, + #migration => instance.migration, + #batch => instance.batch, + _ => throw Exception('Unknown property $field'), + }; +} + +final _typeData = DBEntity( + "migrations", + timestampsEnabled: false, + columns: [ + PrimaryKeyField("id", int, #id), + DBEntityField("migration", String, #migration), + DBEntityField("batch", int, #batch) + ], + mirror: _$MigrationDataEntityMirror.new, + build: (args) => MigrationData( + args[#id], + args[#migration], + args[#batch], + ), +); + +extension MigrationDataQueryExtension on Query { + Future create({ + required String migration, + required int batch, + }) async { + return MigrationQuery.insert({#migration: migration, #batch: batch}); + } + + Future update({ + required WhereBuilder where, + required MigrationData value, + }) async { + final props = { + for (final column in _typeData.columns) + column.dartName: _typeData.mirror(value).get(column.dartName), + }; + + final update = UpdateQuery( + entity.tableName, + whereClause: where(this), + data: conformToDbTypes(props, converters), + ); + + await MigrationQuery.accept(update); + } +} + +Query get MigrationQuery { + Query.addTypeDef(_typeData); + return DB.query(); +} + +CreateSchema get MigrationDataSchema { + Query.addTypeDef(_typeData); + return Schema.fromEntity(); +} From 78279d336962e373c0b482f8cc2c56bd81a5cfa1 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sat, 6 Apr 2024 00:15:58 +0000 Subject: [PATCH 11/50] migrations finally working --- packages/yaroo_cli/lib/{orm => }/orm.dart | 10 +- packages/yaroo_cli/lib/orm/_misc.dart | 8 +- .../lib/orm/commands/migrate_command.dart | 2 +- packages/yaroo_cli/lib/src/migration.dart | 6 +- packages/yaroorm/lib/migration.dart | 36 +++-- .../lib/src/database/entity/converter.dart | 40 +++--- .../lib/src/database/entity/entity.dart | 36 ++--- .../lib/src/database/entity/relations.dart | 127 ++++++++-------- .../lib/src/primitives/_where_impl.dart | 22 +-- .../yaroorm/lib/src/primitives/where.dart | 57 ++++---- packages/yaroorm/lib/src/query/query.dart | 79 ++++------ .../yaroorm/lib/src/query/query_impl.dart | 135 ++++++++---------- .../yaroorm/lib/src/reflection/reflector.dart | 35 +++-- packages/yaroorm/lib/yaroorm.dart | 1 + .../yaroorm/test/integration/e2e_basic.dart | 16 +-- .../test/unit/dialects/sqlite_test.dart | 119 ++++++++------- 16 files changed, 341 insertions(+), 388 deletions(-) rename packages/yaroo_cli/lib/{orm => }/orm.dart (82%) diff --git a/packages/yaroo_cli/lib/orm/orm.dart b/packages/yaroo_cli/lib/orm.dart similarity index 82% rename from packages/yaroo_cli/lib/orm/orm.dart rename to packages/yaroo_cli/lib/orm.dart index 72763969..a582d1de 100644 --- a/packages/yaroo_cli/lib/orm/orm.dart +++ b/packages/yaroo_cli/lib/orm.dart @@ -3,11 +3,11 @@ import 'package:yaroo_cli/orm/commands/migrate_fresh_command.dart'; import 'package:yaroo_cli/src/utils.dart'; import 'package:yaroorm/yaroorm.dart'; -import '../src/logger.dart'; -import 'commands/command.dart'; -import 'commands/migrate_command.dart'; -import 'commands/migrate_reset_command.dart'; -import 'commands/migrate_rollback_command.dart'; +import 'src/logger.dart'; +import 'orm/commands/command.dart'; +import 'orm/commands/migrate_command.dart'; +import 'orm/commands/migrate_reset_command.dart'; +import 'orm/commands/migrate_rollback_command.dart'; const executableName = 'yaroo orm'; const packageName = 'yaroo_cli'; diff --git a/packages/yaroo_cli/lib/orm/_misc.dart b/packages/yaroo_cli/lib/orm/_misc.dart index 62b43f4b..7442d3dd 100644 --- a/packages/yaroo_cli/lib/orm/_misc.dart +++ b/packages/yaroo_cli/lib/orm/_misc.dart @@ -13,10 +13,7 @@ Future hasAlreadyMigratedScript( String scriptName, DatabaseDriver driver, ) async { - final result = await Query.table(DB.config.migrationsTable) - .driver(driver) - .equal('migration', scriptName) - .findOne(); + final result = await MigrationQuery.driver(driver).equal('migration', scriptName).findOne(); return result != null; } @@ -24,6 +21,5 @@ Future getLastBatchNumber( DatabaseDriver driver, String migrationsTable, ) async { - return (await Query.table(migrationsTable).driver(driver).max('batch')) - .toInt(); + return (await MigrationQuery.driver(driver).max('batch')).toInt(); } diff --git a/packages/yaroo_cli/lib/orm/commands/migrate_command.dart b/packages/yaroo_cli/lib/orm/commands/migrate_command.dart index d7c5e913..f02aec75 100644 --- a/packages/yaroo_cli/lib/orm/commands/migrate_command.dart +++ b/packages/yaroo_cli/lib/orm/commands/migrate_command.dart @@ -39,7 +39,7 @@ class MigrateCommand extends OrmCommand { await txnDriver.execute(sql); } - await MigrationQuery.driver(driver) + await MigrationQuery.driver(txnDriver) .create(migration: fileName, batch: batchNos); print('✔ done: $fileName'); diff --git a/packages/yaroo_cli/lib/src/migration.dart b/packages/yaroo_cli/lib/src/migration.dart index 39bbe9f6..3e8fd3ee 100644 --- a/packages/yaroo_cli/lib/src/migration.dart +++ b/packages/yaroo_cli/lib/src/migration.dart @@ -46,8 +46,8 @@ extension MigrationDataQueryExtension on Query { Future create({ required String migration, required int batch, - }) async { - return MigrationQuery.insert({#migration: migration, #batch: batch}); + }) { + return insert({#migration: migration, #batch: batch}); } Future update({ @@ -65,7 +65,7 @@ extension MigrationDataQueryExtension on Query { data: conformToDbTypes(props, converters), ); - await MigrationQuery.accept(update); + await accept(update); } } diff --git a/packages/yaroorm/lib/migration.dart b/packages/yaroorm/lib/migration.dart index afdf89ee..14c64eeb 100644 --- a/packages/yaroorm/lib/migration.dart +++ b/packages/yaroorm/lib/migration.dart @@ -170,22 +170,35 @@ abstract class Schema { static CreateSchema fromEntity() { final entity = Query.getEntity(); - make(TableBlueprint table, DBEntityField field) { + void make(TableBlueprint table, DBEntityField field) { return switch (field.type) { - const (int) => table - ..integer(field.columnName, nullable: field.nullable), - const (double) || const (num) => table - ..double(field.columnName, nullable: field.nullable), - const (DateTime) => table - ..datetime(field.columnName, nullable: field.nullable), - _ => table..string(field.columnName, nullable: field.nullable), + const (int) => table.integer( + field.columnName, + nullable: field.nullable, + ), + const (double) || const (num) => table.double( + field.columnName, + nullable: field.nullable, + ), + const (DateTime) => table.datetime( + field.columnName, + nullable: field.nullable, + ), + _ => table.string( + field.columnName, + nullable: field.nullable, + ), }; } return CreateSchema._( entity.tableName, (table) { - table.id(name: entity.primaryKey.columnName); + table.id( + name: entity.primaryKey.columnName, + autoIncrement: entity.primaryKey.autoIncrement, + ); + for (final prop in entity.columns.where((e) => !e.primaryKey)) { make(table, prop); } @@ -197,9 +210,8 @@ abstract class Schema { static Schema create(String name, TableBluePrintFunc func) => CreateSchema._(name, func); - static Schema dropIfExists(dynamic value) { - // if (value is! String) value = getEntityTableName(value); - return _DropSchema(value); + static Schema dropIfExists(CreateSchema value) { + return _DropSchema(value.tableName); } static Schema rename(String from, String to) => _RenameSchema(from, to); diff --git a/packages/yaroorm/lib/src/database/entity/converter.dart b/packages/yaroorm/lib/src/database/entity/converter.dart index 259e2fdd..371d1fb7 100644 --- a/packages/yaroorm/lib/src/database/entity/converter.dart +++ b/packages/yaroorm/lib/src/database/entity/converter.dart @@ -22,7 +22,6 @@ class DateTimeConverter extends EntityTypeConverter { @override String? toDbType(DateTime? value) { - value = value?.toUtc(); return value == null ? null : '${value.year}-${padValue(value.month)}-${padValue(value.day)} ${padValue(value.hour)}:${padValue(value.minute)}:${padValue(value.second)}'; @@ -42,7 +41,7 @@ class BooleanConverter extends EntityTypeConverter { const dateTimeConverter = DateTimeConverter(); const booleanConverter = BooleanConverter(); -Map _combineConverters( +Map combineConverters( List custom, List driverProvided, ) { @@ -52,38 +51,33 @@ Map _combineConverters( }; } -Map _serializeEntityProps( - Model instance, { - List converters = const [], -}) { +Map conformToDbTypes( + Map data, + Map converters, +) { final entity = Query.getEntity(); - final instanceMirror = entity.mirror(instance); - final allConverters = _combineConverters(entity.converters, converters); - - /// database value conversion back to Dart Types - toDartValue(DBEntityField field) { - final value = instanceMirror.get(field.dartName); - final typeConverter = allConverters[field.type]; + Object toDbType(DBEntityField field) { + final value = data[field.dartName]; + final typeConverter = converters[field.type]; return typeConverter == null ? value : typeConverter.toDbType(value); } return { - for (final entry in entity.columns) entry.columnName: toDartValue(entry), + for (final entry in entity.editableColumns) + entry.columnName: toDbType(entry), }; } -Entity serializedPropsToEntity( - final Map json, { - List converters = const [], -}) { - final entity = Query.getEntity(); - final allConverters = _combineConverters(entity.converters, converters); - +Model serializedPropsToEntity( + Map dataFromDb, + DBEntity entity, + Map converters, +) { final resultsMap = {}; for (final entry in entity.columns) { - final value = json[entry.columnName]; - final typeConverter = allConverters[entry.type]; + final value = dataFromDb[entry.columnName]; + final typeConverter = converters[entry.type]; resultsMap[entry.dartName] = typeConverter == null ? value : typeConverter.fromDbType(value); } diff --git a/packages/yaroorm/lib/src/database/entity/entity.dart b/packages/yaroorm/lib/src/database/entity/entity.dart index 275b7b94..34d787b2 100644 --- a/packages/yaroorm/lib/src/database/entity/entity.dart +++ b/packages/yaroorm/lib/src/database/entity/entity.dart @@ -4,33 +4,20 @@ import 'package:yaroorm/yaroorm.dart'; import 'package:meta/meta_meta.dart'; part 'converter.dart'; -part 'relations.dart'; +// part 'relations.dart'; -abstract class Entity { - String get _foreignKeyForModel => '${Model.toString().camelCase}Id'; +abstract class Entity { + String get _foreignKeyForModel => '${_type.toString().camelCase}Id'; - HasOne hasOne({ - String? foreignKey, - }) { - return HasOne(foreignKey ?? _foreignKeyForModel, this); - } + Type get _type => runtimeType; - HasMany hasMany({ - String? foreignKey, - }) { - return HasMany(foreignKey ?? _foreignKeyForModel, this); - } + // HasOne hasOne({String? foreignKey}) { + // return HasOne(foreignKey ?? _foreignKeyForModel, this); + // } - final DriverContract _driver = DB.defaultDriver; - - Entity withDriver(DriverContract driver) { - return this; - } - - // ignore: non_constant_identifier_names - Map get to_db_data { - return _serializeEntityProps(this, converters: _driver.typeconverters); - } + // HasMany hasMany({String? foreignKey}) { + // return HasMany(foreignKey ?? _foreignKeyForModel, this); + // } } @Target({TargetKind.classType}) @@ -54,7 +41,8 @@ class TableColumn { @Target({TargetKind.field}) class PrimaryKey extends TableColumn { - const PrimaryKey(); + final bool autoIncrement; + const PrimaryKey({this.autoIncrement = true}); } @Target({TargetKind.field}) diff --git a/packages/yaroorm/lib/src/database/entity/relations.dart b/packages/yaroorm/lib/src/database/entity/relations.dart index 5cec67ae..38c6a255 100644 --- a/packages/yaroorm/lib/src/database/entity/relations.dart +++ b/packages/yaroorm/lib/src/database/entity/relations.dart @@ -1,87 +1,82 @@ -part of 'entity.dart'; +// part of 'entity.dart'; -mixin OrderByMixin on EntityRelation - implements OrderByOperation { - @override - K orderByAsc(String field) { - _query.orderByAsc(field); - return this as K; - } +// mixin OrderByMixin on EntityRelation implements OrderByOperation { +// @override +// K orderByAsc(String field) { +// _query.orderByAsc(field); +// return this as K; +// } - @override - K orderByDesc(String field) { - _query.orderByDesc(field); - return this as K; - } -} +// @override +// K orderByDesc(String field) { +// _query.orderByDesc(field); +// return this as K; +// } +// } -abstract class EntityRelation { - final Entity owner; - late final Query _query; +// abstract class EntityRelation { +// final Entity owner; +// late final Query _query; - EntityRelation(this.owner) - : _query = Query.table().driver(owner._driver); +// EntityRelation(this.owner) : _query = Query.table().driver(owner._driver); - Object get ownerId { - final typeData = Query.getEntity(type: owner.runtimeType); - return typeData.mirror.call(owner).get(typeData.primaryKey.dartName)!; - } +// Object get ownerId { +// final typeData = Query.getEntity(type: owner.runtimeType); +// return typeData.mirror.call(owner).get(typeData.primaryKey.dartName)!; +// } - DriverContract get _driver => owner._driver; +// DriverContract get _driver => owner._driver; - get(); +// get(); - delete(); -} +// delete(); +// } -class HasOne extends EntityRelation - with OrderByMixin, RelatedModel> { - final String foreignKey; +// class HasOne extends EntityRelation +// with OrderByMixin, RelatedModel> { +// final String foreignKey; - HasOne(this.foreignKey, super._owner); +// HasOne(this.foreignKey, super._owner); - Future set(RelatedModel model) async { - throw Exception(); - // final data = model.to_db_data..[foreignKey] = ownerId; - // data[model.entityMeta.primaryKey] = await _query.insert(data); - // return serializedPropsToEntity( - // data, - // converters: _driver.typeconverters, - // ).withDriver(_driver) as RelatedModel; - } +// Future set(RelatedModel model) async { +// throw Exception(); +// // final data = model.to_db_data..[foreignKey] = ownerId; +// // data[model.entityMeta.primaryKey] = await _query.insert(data); +// // return serializedPropsToEntity( +// // data, +// // converters: _driver.typeconverters, +// // ).withDriver(_driver) as RelatedModel; +// } - @override - Future get() => - _query.whereEqual(foreignKey, ownerId).findOne(); +// @override +// Future get() => _query.equal(foreignKey, ownerId).findOne(); - @override - Future delete() => _query.whereEqual(foreignKey, ownerId).delete(); -} +// @override +// Future delete() => _query.equal(foreignKey, ownerId).delete(); +// } -class HasMany extends EntityRelation - with OrderByMixin, RelatedModel> { - final String foreignKey; +// class HasMany extends EntityRelation +// with OrderByMixin, RelatedModel> { +// final String foreignKey; - HasMany(this.foreignKey, super.owner); +// HasMany(this.foreignKey, super.owner); - @override - Future> get() { - return _query.whereEqual(foreignKey, ownerId).findMany(); - } +// @override +// Future> get() { +// return _query.equal(foreignKey, ownerId).findMany(); +// } - Future first() => - _query.whereEqual(foreignKey, ownerId).findOne(); +// Future first() => _query.equal(foreignKey, ownerId).findOne(); - Map _serializeModel(RelatedModel model) { - // model.withDriver(_driver); - return model.to_db_data..[foreignKey] = ownerId; - } +// Map _serializeModel(RelatedModel model) { +// // model.withDriver(_driver); +// return model.to_db_data..[foreignKey] = ownerId; +// } - Future add(RelatedModel model) => _query.insert(_serializeModel(model)); +// Future add(RelatedModel model) => _query.insert(_serializeModel(model)); - Future addAll(Iterable model) => - _query.insertMany(model.map(_serializeModel).toList()); +// Future addAll(Iterable model) => _query.insertMany(model.map(_serializeModel).toList()); - @override - Future delete() => _query.whereEqual(foreignKey, ownerId).delete(); -} +// @override +// Future delete() => _query.equal(foreignKey, ownerId).delete(); +// } diff --git a/packages/yaroorm/lib/src/primitives/_where_impl.dart b/packages/yaroorm/lib/src/primitives/_where_impl.dart index ba8e79b1..d7f7ff54 100644 --- a/packages/yaroorm/lib/src/primitives/_where_impl.dart +++ b/packages/yaroorm/lib/src/primitives/_where_impl.dart @@ -1,10 +1,10 @@ part of 'where.dart'; -class _WhereClauseImpl extends WhereClause { +class _WhereClauseImpl extends WhereClause { _WhereClauseImpl(super.query, {super.operator}); @override - WhereClause whereEqual(String field, Value value) { + WhereClause equal(String field, Value value) { final newChild = _WhereClauseImpl(query) ..clauseValue = WhereClauseValue( field, @@ -15,7 +15,7 @@ class _WhereClauseImpl extends WhereClause { } @override - WhereClause whereNotEqual(String field, Value value) { + WhereClause notEqual(String field, Value value) { final newChild = _WhereClauseImpl(query) ..clauseValue = WhereClauseValue( field, @@ -26,7 +26,7 @@ class _WhereClauseImpl extends WhereClause { } @override - WhereClause whereIn(String field, List values) { + WhereClause isIn(String field, List values) { final newChild = _WhereClauseImpl(query) ..clauseValue = WhereClauseValue( field, @@ -37,7 +37,7 @@ class _WhereClauseImpl extends WhereClause { } @override - WhereClause whereNotIn(String field, List values) { + WhereClause isNotIn(String field, List values) { final newChild = _WhereClauseImpl(query) ..clauseValue = WhereClauseValue( field, @@ -48,7 +48,7 @@ class _WhereClauseImpl extends WhereClause { } @override - WhereClause whereBetween(String field, List values) { + WhereClause isBetween(String field, List values) { final newChild = _WhereClauseImpl(query) ..clauseValue = WhereClauseValue( field, @@ -59,7 +59,7 @@ class _WhereClauseImpl extends WhereClause { } @override - WhereClause whereNotBetween(String field, List values) { + WhereClause isNotBetween(String field, List values) { final newChild = _WhereClauseImpl(query) ..clauseValue = WhereClauseValue( field, @@ -70,7 +70,7 @@ class _WhereClauseImpl extends WhereClause { } @override - WhereClause whereLike(String field, String pattern) { + WhereClause isLike(String field, String pattern) { final newChild = _WhereClauseImpl(query) ..clauseValue = WhereClauseValue( field, @@ -81,7 +81,7 @@ class _WhereClauseImpl extends WhereClause { } @override - WhereClause whereNotLike(String field, String pattern) { + WhereClause isNotLike(String field, String pattern) { final newChild = _WhereClauseImpl(query) ..clauseValue = WhereClauseValue( field, (operator: Operator.NOT_LIKE, value: pattern)); @@ -90,7 +90,7 @@ class _WhereClauseImpl extends WhereClause { } @override - WhereClause whereNull(String field) { + WhereClause isNull(String field) { final newChild = _WhereClauseImpl(query) ..clauseValue = WhereClauseValue( field, @@ -101,7 +101,7 @@ class _WhereClauseImpl extends WhereClause { } @override - WhereClause whereNotNull(String field) { + WhereClause isNotNull(String field) { final newChild = _WhereClauseImpl(query) ..clauseValue = WhereClauseValue( field, diff --git a/packages/yaroorm/lib/src/primitives/where.dart b/packages/yaroorm/lib/src/primitives/where.dart index 35158b17..1d34289f 100644 --- a/packages/yaroorm/lib/src/primitives/where.dart +++ b/packages/yaroorm/lib/src/primitives/where.dart @@ -3,11 +3,13 @@ import 'package:yaroorm/src/query/aggregates.dart'; import 'package:yaroorm/yaroorm.dart'; -import '../query/query.dart'; - part '_where_impl.dart'; -mixin WhereOperation { +typedef WhereBuilder = WhereClause Function( + Query query, +); + +mixin WhereOperation { WhereClause where( String field, String condition, [ @@ -20,25 +22,25 @@ mixin WhereOperation { Value? value, ]); - WhereClause whereEqual(String field, Value value); + WhereClause equal(String field, Value value); - WhereClause whereNotEqual(String field, Value value); + WhereClause notEqual(String field, Value value); - WhereClause whereNull(String field); + WhereClause isNull(String field); - WhereClause whereNotNull(String field); + WhereClause isNotNull(String field); - WhereClause whereIn(String field, List values); + WhereClause isIn(String field, List values); - WhereClause whereNotIn(String field, List values); + WhereClause isNotIn(String field, List values); - WhereClause whereLike(String field, String pattern); + WhereClause isLike(String field, String pattern); - WhereClause whereNotLike(String field, String pattern); + WhereClause isNotLike(String field, String pattern); - WhereClause whereBetween(String field, List values); + WhereClause isBetween(String field, List values); - WhereClause whereNotBetween(String field, List values); + WhereClause isNotBetween(String field, List values); Query orWhereFunc(Function(Query query) builder); @@ -87,8 +89,7 @@ Operator _strToOperator(String condition) => switch (condition) { // 'between' => Operator.BETWEEN, 'not between' => Operator.NOT_BETWEEN, - _ => throw ArgumentError.value( - condition, null, 'Condition $condition is not known') + _ => throw ArgumentError.value(condition, null, 'Condition $condition is not known') }; class WhereClauseValue { @@ -115,24 +116,15 @@ class WhereClauseValue { } } -abstract class WhereClause - with - WhereOperation, - FindOperation, - LimitOperation, - OrderByOperation>, - AggregateOperation { - final List>> children = []; +abstract class WhereClause + with WhereOperation, FindOperation, LimitOperation, OrderByOperation>, AggregateOperation { + final List>> children = []; - List>> get group => children.isEmpty + List>> get group => children.isEmpty ? const [] : [ if (clauseValue != null) - ( - operator, - WhereClause.create(query, operator: operator) - ..clauseValue = clauseValue - ), + (operator, WhereClause.create(query, operator: operator)..clauseValue = clauseValue), ...children ]; @@ -141,7 +133,7 @@ abstract class WhereClause if (children.isNotEmpty) ...children.map((e) => e.$1), }; - final Query query; + final Query query; final LogicalOperator operator; @@ -149,13 +141,12 @@ abstract class WhereClause WhereClause(this.query, {this.operator = LogicalOperator.AND}); - static WhereClause create( + static WhereClause create( Query query, { LogicalOperator operator = LogicalOperator.AND, WhereClauseValue? value, }) { - return _WhereClauseImpl(query, operator: operator) - ..clauseValue = value; + return _WhereClauseImpl(query, operator: operator)..clauseValue = value; } Future delete(); diff --git a/packages/yaroorm/lib/src/query/query.dart b/packages/yaroorm/lib/src/query/query.dart index 6e92a6b2..6a3f9a62 100644 --- a/packages/yaroorm/lib/src/query/query.dart +++ b/packages/yaroorm/lib/src/query/query.dart @@ -20,8 +20,8 @@ mixin FindOperation { Future> findMany(); } -mixin InsertOperation { - Future insert(Map data); +mixin InsertOperation { + Future insert(Map data); Future insertMany(List> values); } @@ -30,17 +30,6 @@ mixin LimitOperation { Future> take(int limit); } -mixin UpdateOperation { - UpdateQuery update({ - required WhereClause Function(Query query) where, - required Map values, - }); -} - -mixin DeleteOperation { - DeleteQuery delete(WhereClause Function(Query query) where); -} - typedef OrderBy = ({String field, OrderByDirection direction}); mixin OrderByOperation { @@ -76,21 +65,25 @@ sealed class QueryBase { String get statement; } -abstract interface class Query extends QueryBase> +abstract interface class Query extends QueryBase> with - ReadOperation, - WhereOperation, - LimitOperation, - OrderByOperation>, - InsertOperation, - DeleteOperation, - UpdateOperation, + ReadOperation, + WhereOperation, + LimitOperation, + OrderByOperation>, + InsertOperation, AggregateOperation { final Set fieldSelections; final Set orderByProps; - final List> whereClauses; + final List> whereClauses; + final DBEntity entity; + + Map get converters => combineConverters( + entity.converters, + runner.typeconverters, + ); - static final Map _typedata = {}; + static final Map _typedatas = {}; // ignore: prefer_final_fields int? _limit; @@ -98,7 +91,8 @@ abstract interface class Query extends QueryBase> int? get limit => _limit; Query(super.tableName) - : fieldSelections = {}, + : entity = Query.getEntity(), + fieldSelections = {}, orderByProps = {}, whereClauses = [], _limit = null; @@ -111,46 +105,29 @@ abstract interface class Query extends QueryBase> return QueryImpl(tableName!); } - Query select(List selections) { + Query select(List selections) { fieldSelections.addAll(selections); return this; } - @override - DeleteQuery delete( - WhereClause Function(Query query) where, - ) { - return DeleteQuery( - tableName, - whereClause: where(this), - ).driver(runner); + Future accept>(A query) async { + return (query..driver(runner)).execute(); } - @override - UpdateQuery update({ - required WhereClause Function(Query query) where, - required Map values, - }) { - return UpdateQuery( - tableName, - whereClause: where(this), - data: values, - ).driver(runner); - } - - static void addTypeDef(DBEntity entity) { + static void addTypeDef(DBEntity entity) { var type = T; if (type == Entity) type = entity.dartType; if (type == Entity) throw Exception(); - _typedata[type] = entity; + _typedatas[type] = entity; } @internal - static DBEntity getEntity({Type? type}) { + static DBEntity getEntity({Type? type}) { type ??= T; - return _typedata.containsKey(type) - ? _typedata[type]! - : throw Exception('Type Data not found for $type'); + if (!_typedatas.containsKey(type)) { + throw Exception('Type Data not found for $type'); + } + return _typedatas[type]! as DBEntity; } } diff --git a/packages/yaroorm/lib/src/query/query_impl.dart b/packages/yaroorm/lib/src/query/query_impl.dart index 43dfe0e2..3d230a4b 100644 --- a/packages/yaroorm/lib/src/query/query_impl.dart +++ b/packages/yaroorm/lib/src/query/query_impl.dart @@ -2,93 +2,91 @@ part of 'query.dart'; enum OrderByDirection { asc, desc } -class QueryImpl extends Query { +class QueryImpl extends Query { QueryImpl(super.tableName); @override - WhereClause where( + WhereClause where( String field, String condition, [ Value? value, ]) { - final newClause = WhereClause.create(this, - value: WhereClauseValue.from(field, condition, value)); + final newClause = WhereClause.create(this, value: WhereClauseValue.from(field, condition, value)); whereClauses.add(newClause); return newClause; } @override - Query orderByAsc(String field) { + Query orderByAsc(String field) { orderByProps.add((field: field, direction: OrderByDirection.asc)); return this; } @override - Query orderByDesc(String field) { + Query orderByDesc(String field) { orderByProps.add((field: field, direction: OrderByDirection.desc)); return this; } @override - Future insert(Map data) async { - final recordId = await runner.insert(InsertQuery(tableName, data: data)); - return recordId as PrimaryKey; + Future insert(Map data) async { + final dataToDbD = conformToDbTypes(data, converters); + final recordId = await runner.insert(InsertQuery(tableName, data: dataToDbD)); + return (await get(recordId))!; } @override Future insertMany(List> values) async { - return runner.insertMany(InsertManyQuery(tableName, values: values)); + throw Exception(); } @override - Future> all({int? limit}) async { + Future> all({int? limit}) async { final results = await runner.query(this.._limit = limit); - if (results.isEmpty) return []; - return results.map(_wrapRawResult).toList(); + if (results.isEmpty) return []; + return results.map(_wrapRawResult).toList(); } @override - Future> take(int limit) async { + Future> take(int limit) async { final results = await runner.query(this.._limit = limit); - if (results.isEmpty) return []; - return results.map(_wrapRawResult).toList(); + if (results.isEmpty) return []; + return results.map(_wrapRawResult).toList(); } @override - Future get([dynamic id]) async { + Future get([dynamic id]) async { if (id != null) { - return whereEqual(getEntityPrimaryKey(), id).findOne(); + return equal(getEntityPrimaryKey(), id).findOne(); } return (await take(1)).firstOrNull; } /// [T] is the expected type passed to [Query] via Query - T _wrapRawResult(Map? result) { - if (T == dynamic || result == null) return result as dynamic; - return (serializedPropsToEntity( + Model _wrapRawResult(Map? result) { + if (result == null) return result as dynamic; + return serializedPropsToEntity( result, - converters: runner.typeconverters, - )).withDriver(runner) as T; + entity, + converters, + ); } @override - WhereClause orWhere(String field, String condition, - [Value? value]) { - throw StateError( - 'Cannot use `orWhere` directly on a Query you need a WHERE clause first'); + WhereClause orWhere(String field, String condition, [Value? value]) { + throw StateError('Cannot use `orWhere` directly on a Query you need a WHERE clause first'); } @override - Query orWhereFunc(Function(Query query) builder) { + Query orWhereFunc(Function(Query query) builder) { if (whereClauses.isEmpty) { throw StateError('Cannot use `orWhereFunc` without a where clause'); } - final newQuery = QueryImpl(tableName); + final newQuery = QueryImpl(tableName); builder(newQuery); - final newGroup = - WhereClause.create(this, operator: LogicalOperator.OR); + final newGroup = WhereClause.create(this, operator: LogicalOperator.OR); for (final clause in newQuery.whereClauses) { newGroup.children.add((clause.operator, clause)); } @@ -99,12 +97,11 @@ class QueryImpl extends Query { } @override - Query whereFunc(Function(Query query) builder) { - final newQuery = QueryImpl(tableName); + Query whereFunc(Function(Query query) builder) { + final newQuery = QueryImpl(tableName); builder(newQuery); - final newGroup = - WhereClause.create(this, operator: LogicalOperator.AND); + final newGroup = WhereClause.create(this, operator: LogicalOperator.AND); for (final clause in newQuery.whereClauses) { newGroup.children.add((clause.operator, clause)); } @@ -115,89 +112,81 @@ class QueryImpl extends Query { } @override - WhereClause whereEqual(String field, Value value) { - final newClause = WhereClause.create(this, - value: - WhereClauseValue(field, (operator: Operator.EQUAL, value: value))); + WhereClause equal(String field, Value value) { + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.EQUAL, value: value))); whereClauses.add(newClause); return newClause; } @override - WhereClause whereNotEqual(String field, Value value) { - final newClause = WhereClause.create(this, - value: WhereClauseValue( - field, (operator: Operator.NOT_EQUAL, value: value))); + WhereClause notEqual(String field, Value value) { + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NOT_EQUAL, value: value))); whereClauses.add(newClause); return newClause; } @override - WhereClause whereIn(String field, List values) { - final newClause = WhereClause.create(this, - value: WhereClauseValue(field, (operator: Operator.IN, value: values))); + WhereClause isIn(String field, List values) { + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.IN, value: values))); whereClauses.add(newClause); return newClause; } @override - WhereClause whereNotIn(String field, List values) { - final newClause = WhereClause.create(this, - value: WhereClauseValue( - field, (operator: Operator.NOT_IN, value: values))); + WhereClause isNotIn(String field, List values) { + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NOT_IN, value: values))); whereClauses.add(newClause); return newClause; } @override - WhereClause whereLike(String field, String pattern) { - final newClause = WhereClause.create(this, - value: - WhereClauseValue(field, (operator: Operator.LIKE, value: pattern))); + WhereClause isLike(String field, String pattern) { + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.LIKE, value: pattern))); whereClauses.add(newClause); return newClause; } @override - WhereClause whereNotLike(String field, String pattern) { - final newClause = WhereClause.create(this, - value: WhereClauseValue( - field, (operator: Operator.NOT_LIKE, value: pattern))); + WhereClause isNotLike(String field, String pattern) { + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NOT_LIKE, value: pattern))); whereClauses.add(newClause); return newClause; } @override - WhereClause whereNull(String field) { - final newClause = WhereClause.create(this, - value: WhereClauseValue(field, (operator: Operator.NULL, value: null))); + WhereClause isNull(String field) { + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NULL, value: null))); whereClauses.add(newClause); return newClause; } @override - WhereClause whereNotNull(String field) { - final newClause = WhereClause.create(this, - value: WhereClauseValue( - field, (operator: Operator.NOT_NULL, value: null))); + WhereClause isNotNull(String field) { + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NOT_NULL, value: null))); whereClauses.add(newClause); return newClause; } @override - WhereClause whereBetween(String field, List values) { - final newClause = WhereClause.create(this, - value: WhereClauseValue( - field, (operator: Operator.BETWEEN, value: values))); + WhereClause isBetween(String field, List values) { + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.BETWEEN, value: values))); whereClauses.add(newClause); return newClause; } @override - WhereClause whereNotBetween(String field, List values) { - final newClause = WhereClause.create(this, - value: WhereClauseValue( - field, (operator: Operator.NOT_BETWEEN, value: values))); + WhereClause isNotBetween(String field, List values) { + final newClause = WhereClause.create(this, + value: WhereClauseValue(field, (operator: Operator.NOT_BETWEEN, value: values))); whereClauses.add(newClause); return newClause; } diff --git a/packages/yaroorm/lib/src/reflection/reflector.dart b/packages/yaroorm/lib/src/reflection/reflector.dart index c64b013a..2b3661c5 100644 --- a/packages/yaroorm/lib/src/reflection/reflector.dart +++ b/packages/yaroorm/lib/src/reflection/reflector.dart @@ -29,7 +29,10 @@ class DBEntity { final bool timestampsEnabled; - DBEntityField get primaryKey => columns.firstWhere((e) => e.primaryKey); + PrimaryKeyField get primaryKey => columns.whereType().first; + + List get editableColumns => + columns.where((e) => e != primaryKey).toList(); const DBEntity( this.tableName, { @@ -41,7 +44,7 @@ class DBEntity { }); } -class DBEntityField { +final class DBEntityField { /// dart name for property on Entity class final Symbol dartName; @@ -52,13 +55,23 @@ class DBEntityField { final Type type; final bool nullable; - final bool primaryKey; - - const DBEntityField( - this.columnName, - this.type, - this.dartName, { - required this.nullable, - this.primaryKey = false, - }); + + bool get primaryKey => false; + + const DBEntityField(this.columnName, this.type, this.dartName, + {this.nullable = false}); +} + +final class PrimaryKeyField extends DBEntityField { + final bool autoIncrement; + + const PrimaryKeyField( + super.columnName, + super.type, + super.dartName, { + this.autoIncrement = true, + }) : super(nullable: false); + + @override + bool get primaryKey => true; } diff --git a/packages/yaroorm/lib/yaroorm.dart b/packages/yaroorm/lib/yaroorm.dart index 1d0bd493..39af14a4 100644 --- a/packages/yaroorm/lib/yaroorm.dart +++ b/packages/yaroorm/lib/yaroorm.dart @@ -1,6 +1,7 @@ library; export 'src/query/query.dart'; +export 'src/primitives/where.dart' show WhereBuilder; export 'src/reflection/reflector.dart'; export 'src/database/driver/driver.dart'; export 'src/database/entity/entity.dart'; diff --git a/packages/yaroorm/test/integration/e2e_basic.dart b/packages/yaroorm/test/integration/e2e_basic.dart index d0dd2a81..e2817e0c 100644 --- a/packages/yaroorm/test/integration/e2e_basic.dart +++ b/packages/yaroorm/test/integration/e2e_basic.dart @@ -41,7 +41,7 @@ void runBasicE2ETest(String connectionName) { }); group('Aggregate Functions', () { - final query = db.query().whereLike('home_address', '%%, Ghana'); + final query = db.query().isLike('home_address', '%%, Ghana'); List usersInGhana = []; setUpAll(() async { @@ -107,13 +107,13 @@ void runBasicE2ETest(String connectionName) { test('should update many users', () async { final userQuery = db.query(); - final age50Users = userQuery.whereEqual('age', 50); + final age50Users = userQuery.equal('age', 50); final usersWithAge50 = await age50Users.findMany(); expect(usersWithAge50.length, 4); expect(usersWithAge50.every((e) => e.age == 50), isTrue); await userQuery.update( - where: (query) => query.whereEqual('age', 50), + where: (query) => query.equal('age', 50), values: {'home_address': 'Keta, Ghana'}).execute(); final updatedResult = await age50Users.findMany(); @@ -126,7 +126,7 @@ void runBasicE2ETest(String connectionName) { test('should fetch only users in Ghana', () async { final query = db .query() - .whereLike('home_address', '%, Ghana') + .isLike('home_address', '%, Ghana') .orderByDesc('age'); final usersInGhana = await query.findMany(); expect(usersInGhana.length, 10); @@ -141,7 +141,7 @@ void runBasicE2ETest(String connectionName) { test('should get all users between age 35 and 50', () async { final age50Users = await db .query() - .whereBetween('age', [35, 50]) + .isBetween('age', [35, 50]) .orderByDesc('age') .findMany(); expect(age50Users.length, 19); @@ -152,7 +152,7 @@ void runBasicE2ETest(String connectionName) { test('should get all users in somewhere in Nigeria', () async { final users = await db .query() - .whereLike('home_address', '%, Nigeria') + .isLike('home_address', '%, Nigeria') .orderByAsc('home_address') .findMany(); @@ -164,7 +164,7 @@ void runBasicE2ETest(String connectionName) { test('should get all users where age is 30 or 52', () async { final users = await db .query() - .whereEqual('age', 30) + .equal('age', 30) .orWhere('age', '=', 52) .findMany(); expect(users.every((e) => [30, 52].contains(e.age)), isTrue); @@ -181,7 +181,7 @@ void runBasicE2ETest(String connectionName) { }); test('should delete many users', () async { - final query = db.query().whereLike('home_address', '%, Nigeria'); + final query = db.query().isLike('home_address', '%, Nigeria'); expect(await query.findMany(), isNotEmpty); await query.delete(); diff --git a/packages/yaroorm/test/unit/dialects/sqlite_test.dart b/packages/yaroorm/test/unit/dialects/sqlite_test.dart index 59b608f4..385650f9 100644 --- a/packages/yaroorm/test/unit/dialects/sqlite_test.dart +++ b/packages/yaroorm/test/unit/dialects/sqlite_test.dart @@ -171,7 +171,7 @@ void main() { final query = Query.table('users') .driver(driver) .where('firstname', '=', 'Chima') - .whereNull('age'); + .isNull('age'); expect( query.statement, @@ -183,7 +183,7 @@ void main() { final query = Query.table('users') .driver(driver) .where('firstname', '=', 'Chima') - .whereNotNull('age'); + .isNotNull('age'); expect( query.statement, @@ -195,7 +195,7 @@ void main() { final query = Query.table('users') .driver(driver) .where('firstname', '=', 'Chima') - .whereIn('age', [22, 24, 25]); + .isIn('age', [22, 24, 25]); expect( query.statement, @@ -207,7 +207,7 @@ void main() { final query = Query.table('users') .driver(driver) .where('firstname', '=', 'Chima') - .whereNotIn('age', [22, 24, 25]); + .isNotIn('age', [22, 24, 25]); expect( query.statement, @@ -219,7 +219,7 @@ void main() { final query = Query.table('users') .driver(driver) .where('firstname', '=', 'Chima') - .whereLike('lastname', 'hello%'); + .isLike('lastname', 'hello%'); expect( query.statement, @@ -231,7 +231,7 @@ void main() { final query = Query.table('users') .driver(driver) .where('firstname', '=', 'Chima') - .whereNotLike('lastname', 'hello%'); + .isNotLike('lastname', 'hello%'); expect( query.statement, @@ -243,7 +243,7 @@ void main() { final query = Query.table('users') .driver(driver) .where('firstname', '=', 'Chima') - .whereBetween('lastname', [22, 50]); + .isBetween('lastname', [22, 50]); expect( query.statement, @@ -255,7 +255,7 @@ void main() { final query = Query.table('users') .driver(driver) .where('firstname', '=', 'Chima') - .whereNotBetween('lastname', [22.34, 50]); + .isNotBetween('lastname', [22.34, 50]); expect( query.statement, @@ -424,7 +424,7 @@ void main() { test('of level 1', () { final query = Query.table('users') .driver(driver) - .whereIn('firstname', ['Accra', 'Tamale']); + .isIn('firstname', ['Accra', 'Tamale']); expect( query.statement, @@ -433,7 +433,7 @@ void main() { }); test('of level 2', () { - final query = Query.table('users').driver(driver).whereIn( + final query = Query.table('users').driver(driver).isIn( 'places', ['Accra', 'Tamale']).where('lastname', '=', 'Precious'); expect( @@ -445,7 +445,7 @@ void main() { test('of level 3', () { final query = Query.table('users') .driver(driver) - .whereIn('places', ['Accra', 'Tamale']) + .isIn('places', ['Accra', 'Tamale']) .where('lastname', '=', 'Precious') .where('names', 'like', 'Hello%'); @@ -458,7 +458,7 @@ void main() { test('of level 4', () { final query = Query.table('users') .driver(driver) - .whereIn('places', ['Accra', 'Tamale']) + .isIn('places', ['Accra', 'Tamale']) .where('lastname', '=', 'Precious') .where('names', 'like', 'Hello%') .orWhere('age', 'in', [23, 34, 55]); @@ -474,7 +474,7 @@ void main() { test('of level 1', () { final query = Query.table('users') .driver(driver) - .whereNotIn('firstname', ['Accra', 'Tamale']); + .isNotIn('firstname', ['Accra', 'Tamale']); expect( query.statement, @@ -483,7 +483,7 @@ void main() { }); test('of level 2', () { - final query = Query.table('users').driver(driver).whereNotIn( + final query = Query.table('users').driver(driver).isNotIn( 'places', ['Accra', 'Tamale']).where('lastname', '=', 'Precious'); expect( @@ -495,9 +495,9 @@ void main() { test('of level 3', () { final query = Query.table('users') .driver(driver) - .whereNotIn('places', ['Accra', 'Tamale']) + .isNotIn('places', ['Accra', 'Tamale']) .where('lastname', '=', 'Precious') - .whereNotNull('names'); + .isNotNull('names'); expect( query.statement, @@ -508,10 +508,10 @@ void main() { test('of level 4', () { final query = Query.table('users') .driver(driver) - .whereNotIn('places', ['Accra', 'Tamale']) + .isNotIn('places', ['Accra', 'Tamale']) .where('lastname', '=', 'Precious') .where('names', 'like', 'Hello%') - .whereBetween('age', [23, 34]); + .isBetween('age', [23, 34]); expect( query.statement, @@ -525,14 +525,14 @@ void main() { expect( () => Query.table('users') .driver(driver) - .whereBetween('age', [22]).statement, + .isBetween('age', [22]).statement, throwsA(isA().having((p0) => p0.message, '', 'BETWEEN requires a List with length 2 (val1, val2)'))); }); test('of level 1', () { final query = - Query.table('users').driver(driver).whereBetween('age', [22, 70]); + Query.table('users').driver(driver).isBetween('age', [22, 70]); expect( query.statement, @@ -543,7 +543,7 @@ void main() { test('of level 2', () { final query = Query.table('users') .driver(driver) - .whereBetween('places', ['Accra', 'Tamale']).where( + .isBetween('places', ['Accra', 'Tamale']).where( 'lastname', 'between', [2, 100]); expect( @@ -555,7 +555,7 @@ void main() { test('of level 3', () { final query = Query.table('users') .driver(driver) - .whereIn('places', ['Accra', 'Tamale']).whereBetween( + .isIn('places', ['Accra', 'Tamale']).isBetween( 'lastname', [22, 48]).where('names', 'like', 'Hello%'); expect( @@ -567,10 +567,10 @@ void main() { test('of level 4', () { final query = Query.table('users') .driver(driver) - .whereIn('places', ['Accra', 'Tamale']) + .isIn('places', ['Accra', 'Tamale']) .where('lastname', '=', 'Precious') .orWhere('age', 'in', [23, 34, 55]) - .whereBetween('dates', ['2015-01-01', '2016-12-01']); + .isBetween('dates', ['2015-01-01', '2016-12-01']); expect( query.statement, @@ -584,15 +584,14 @@ void main() { expect( () => Query.table('users') .driver(driver) - .whereNotBetween('age', [22]).statement, + .isNotBetween('age', [22]).statement, throwsA(isA().having((p0) => p0.message, '', 'NOT_BETWEEN requires a List with length 2 (val1, val2)'))); }); test('of level 1', () { - final query = Query.table('users') - .driver(driver) - .whereNotBetween('age', [22, 70]); + final query = + Query.table('users').driver(driver).isNotBetween('age', [22, 70]); expect( query.statement, @@ -603,7 +602,7 @@ void main() { test('of level 2', () { final query = Query.table('users') .driver(driver) - .whereNotBetween('places', ['Accra', 'Tamale']).where( + .isNotBetween('places', ['Accra', 'Tamale']).where( 'lastname', 'between', [2, 100]); expect( @@ -615,7 +614,7 @@ void main() { test('of level 3', () { final query = Query.table('users') .driver(driver) - .whereIn('places', ['Accra', 'Tamale']).whereNotBetween( + .isIn('places', ['Accra', 'Tamale']).isNotBetween( 'lastname', [22, 48]).where('names', 'like', 'Hello%'); expect( @@ -627,10 +626,10 @@ void main() { test('of level 4', () { final query = Query.table('users') .driver(driver) - .whereIn('places', ['Accra', 'Tamale']) + .isIn('places', ['Accra', 'Tamale']) .where('lastname', '=', 'Precious') .orWhere('age', 'in', [23, 34, 55]) - .whereNotBetween('dates', ['2015-01-01', '2016-12-01']); + .isNotBetween('dates', ['2015-01-01', '2016-12-01']); expect( query.statement, @@ -641,9 +640,8 @@ void main() { group('when .whereLike', () { test('of level 1', () { - final query = Query.table('users') - .driver(driver) - .whereLike('firstname', 'Names%%'); + final query = + Query.table('users').driver(driver).isLike('firstname', 'Names%%'); expect( query.statement, @@ -654,7 +652,7 @@ void main() { test('of level 2', () { final query = Query.table('users') .driver(driver) - .whereLike('places', 'Chima**') + .isLike('places', 'Chima**') .where('lastname', '=', 'Precious'); expect( @@ -666,7 +664,7 @@ void main() { test('of level 3', () { final query = Query.table('users') .driver(driver) - .whereLike('places', 'Hello123') + .isLike('places', 'Hello123') .where('lastname', '=', 'Precious') .where('names', 'like', 'Hello%'); @@ -679,7 +677,7 @@ void main() { test('of level 4', () { final query = Query.table('users') .driver(driver) - .whereLike('places', 'Nems#') + .isLike('places', 'Nems#') .where('lastname', '=', 'Precious') .where('names', 'like', 'Hello%') .orWhere('age', 'between', [23, 34]); @@ -695,7 +693,7 @@ void main() { test('of level 1', () { final query = Query.table('users') .driver(driver) - .whereNotLike('firstname', 'Names%%'); + .isNotLike('firstname', 'Names%%'); expect( query.statement, @@ -706,8 +704,8 @@ void main() { test('of level 2', () { final query = Query.table('users') .driver(driver) - .whereNotLike('places', 'Chima**') - .whereBetween('lastname', [12, 90]); + .isNotLike('places', 'Chima**') + .isBetween('lastname', [12, 90]); expect( query.statement, @@ -718,7 +716,7 @@ void main() { test('of level 3', () { final query = Query.table('users') .driver(driver) - .whereNotLike('places', 'Hello123') + .isNotLike('places', 'Hello123') .where('lastname', '=', 'Precious') .where('names', 'like', 'Hello%'); @@ -731,7 +729,7 @@ void main() { test('of level 4', () { final query = Query.table('users') .driver(driver) - .whereNotLike('places', 'Nems#') + .isNotLike('places', 'Nems#') .where('lastname', '=', 'Precious') .orWhere('names', 'not like', 'Hello%') .orWhere('age', 'between', [23, 34]); @@ -745,8 +743,7 @@ void main() { group('when .whereNull', () { test('of level 1', () { - final query = - Query.table('users').driver(driver).whereNull('firstname'); + final query = Query.table('users').driver(driver).isNull('firstname'); expect(query.statement, 'SELECT * FROM users WHERE firstname IS NULL;'); }); @@ -754,7 +751,7 @@ void main() { test('of level 2', () { final query = Query.table('users') .driver(driver) - .whereNull('places') + .isNull('places') .where('lastname', '=', 'Precious'); expect( @@ -766,7 +763,7 @@ void main() { test('of level 3', () { final query = Query.table('users') .driver(driver) - .whereNull('places') + .isNull('places') .where('lastname', '=', 'Precious') .where('names', 'like', 'Hello%'); @@ -779,7 +776,7 @@ void main() { test('of level 4', () { final query = Query.table('users') .driver(driver) - .whereNull('places') + .isNull('places') .where('lastname', '=', 'Precious') .orWhere('names', 'null') .orWhere('age', 'between', [23, 34]); @@ -794,7 +791,7 @@ void main() { group('when .whereNotNull', () { test('of level 1', () { final query = - Query.table('users').driver(driver).whereNotNull('firstname'); + Query.table('users').driver(driver).isNotNull('firstname'); expect( query.statement, @@ -805,7 +802,7 @@ void main() { test('of level 2', () { final query = Query.table('users') .driver(driver) - .whereNotNull('places') + .isNotNull('places') .where('lastname', '=', 'Precious'); expect( @@ -817,7 +814,7 @@ void main() { test('of level 3', () { final query = Query.table('users') .driver(driver) - .whereNotNull('places') + .isNotNull('places') .where('lastname', '=', 'Precious') .where('names', 'like', 'Hello%'); @@ -830,7 +827,7 @@ void main() { test('of level 4', () { final query = Query.table('users') .driver(driver) - .whereNotNull('places') + .isNotNull('places') .where('lastname', '=', 'Precious') .orWhere('names', 'not null') .orWhere('age', 'between', [23, 34]); @@ -965,10 +962,10 @@ void main() { .where('image', 'like', 'Image&&'), ) .whereFunc((query) => query - .whereIn('fruits', ['oranges', 'apples']) - .whereBetween('price', [20, 100]) - .whereEqual('status', 'available') - .whereLike('stores', 'Accra, %%')); + .isIn('fruits', ['oranges', 'apples']) + .isBetween('price', [20, 100]) + .equal('status', 'available') + .isLike('stores', 'Accra, %%')); final sB = StringBuffer(); sB.write("SELECT * FROM users WHERE name = 'Chima' "); @@ -991,16 +988,16 @@ void main() { .where('image', 'like', 'Image&&'), ) .whereFunc((query) => query - .whereIn('fruits', ['oranges', 'apples']) - .whereBetween('price', [20, 100]) - .whereEqual('status', 'available') - .whereLike('stores', 'Accra, %%')) + .isIn('fruits', ['oranges', 'apples']) + .isBetween('price', [20, 100]) + .equal('status', 'available') + .isLike('stores', 'Accra, %%')) .orWhereFunc((query) => query .where('languages', 'in', ['python', 'cobra']).orWhereFunc( (query) => query .where('job_status', '=', 'available') .where('location', '=', 'Accra') - .whereNotBetween('salary', [8000, 16000]))); + .isNotBetween('salary', [8000, 16000]))); final sB = StringBuffer(); sB.write("SELECT * FROM users WHERE name = 'Chima' "); From 2eb035bf0176f19e1c238e69306417cc12a28370 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sat, 6 Apr 2024 09:46:55 +0000 Subject: [PATCH 12/50] wip --- .../yaroo_cli/lib/src/command_runner.dart | 3 + .../yaroo_cli/lib/src/commands/generate.dart | 19 + packages/yaroo_cli/lib/src/migration.dart | 3 + packages/yaroo_cli/lib/src/type_analyzer.dart | 324 ++++++++++++++ packages/yaroo_cli/pubspec.yaml | 7 + packages/yaroorm/.gitignore | 3 +- packages/yaroorm/lib/migration.dart | 2 + .../lib/src/database/entity/entity.dart | 4 +- .../lib/src/primitives/_where_impl.dart | 7 - packages/yaroorm/lib/src/query/query.dart | 1 + .../yaroorm/lib/src/query/query_impl.dart | 64 +-- .../reflector.dart => reflection.dart} | 2 +- packages/yaroorm/lib/yaroorm.dart | 1 - packages/yaroorm/pubspec.yaml | 3 + .../yaroorm/test/integration/e2e_basic.dart | 10 +- .../test/integration/e2e_relation.dart | 336 +++++++-------- .../test/integration/fixtures/migrations.dart | 2 +- .../test/integration/fixtures/test_data.dart | 394 ++++++++---------- .../yaroorm/test/integration/mariadb.e2e.dart | 6 +- .../yaroorm/test/integration/mysql.e2e.dart | 6 +- .../yaroorm/test/integration/pgsql.e2e.dart | 6 +- .../yaroorm/test/integration/sqlite.e2e.dart | 7 +- packages/yaroorm/test/models/models.dart | 66 +++ 23 files changed, 834 insertions(+), 442 deletions(-) create mode 100644 packages/yaroo_cli/lib/src/commands/generate.dart create mode 100644 packages/yaroo_cli/lib/src/type_analyzer.dart rename packages/yaroorm/lib/src/{reflection/reflector.dart => reflection.dart} (97%) create mode 100644 packages/yaroorm/test/models/models.dart diff --git a/packages/yaroo_cli/lib/src/command_runner.dart b/packages/yaroo_cli/lib/src/command_runner.dart index e1842453..db5f57d4 100644 --- a/packages/yaroo_cli/lib/src/command_runner.dart +++ b/packages/yaroo_cli/lib/src/command_runner.dart @@ -1,5 +1,6 @@ import 'package:cli_completion/cli_completion.dart'; import 'package:mason_logger/mason_logger.dart'; +import 'package:yaroo_cli/src/commands/generate.dart'; import 'package:yaroo_cli/src/logger.dart'; const executableName = 'yaroo'; @@ -16,6 +17,8 @@ class YarooCliCommandRunner extends CompletionCommandRunner { callback: (verbose) { if (verbose) logger.level = Level.verbose; }); + + addCommand(GenerateEntityCommand()); } @override diff --git a/packages/yaroo_cli/lib/src/commands/generate.dart b/packages/yaroo_cli/lib/src/commands/generate.dart new file mode 100644 index 00000000..f2efeef1 --- /dev/null +++ b/packages/yaroo_cli/lib/src/commands/generate.dart @@ -0,0 +1,19 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:args/command_runner.dart'; +import 'package:yaroo_cli/src/type_analyzer.dart'; + +class GenerateEntityCommand extends Command { + @override + String get description => 'Generate entity related code'; + + @override + String get name => 'generate'; + + @override + Future run() async { + await EntityAnalyzer(Directory.current).analyze(); + return 0; + } +} diff --git a/packages/yaroo_cli/lib/src/migration.dart b/packages/yaroo_cli/lib/src/migration.dart index 3e8fd3ee..3bc31f38 100644 --- a/packages/yaroo_cli/lib/src/migration.dart +++ b/packages/yaroo_cli/lib/src/migration.dart @@ -3,6 +3,9 @@ import 'package:yaroorm/migration.dart'; import 'package:yaroorm/yaroorm.dart'; +// ignore: implementation_imports +import 'package:yaroorm/src/reflection.dart'; + class MigrationData extends Entity { @primaryKey final int id; diff --git a/packages/yaroo_cli/lib/src/type_analyzer.dart b/packages/yaroo_cli/lib/src/type_analyzer.dart new file mode 100644 index 00000000..99df770e --- /dev/null +++ b/packages/yaroo_cli/lib/src/type_analyzer.dart @@ -0,0 +1,324 @@ +import 'dart:io'; + +import 'package:analyzer/dart/analysis/analysis_context_collection.dart'; +import 'package:analyzer/dart/analysis/results.dart'; +import 'package:analyzer/dart/constant/value.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:analyzer/file_system/physical_file_system.dart'; +import 'package:code_builder/code_builder.dart'; +import 'package:dart_style/dart_style.dart'; +import 'package:path/path.dart' as path; +import 'package:recase/recase.dart'; +import 'package:grammer/grammer.dart'; +import 'package:collection/collection.dart'; + +const yaroormIdentifier = 'package:yaroorm/src/database/entity/entity.dart'; + +class EntityAnalyzer { + final AnalysisContextCollection collection; + + EntityAnalyzer(Directory projectDir) + : collection = AnalysisContextCollection( + includedPaths: [ + path.join(projectDir.path, 'lib/src/models'), + path.join(projectDir.path, 'test/models'), + ], + resourceProvider: PhysicalResourceProvider.INSTANCE, + ); + + Future analyze() async { + await for (var (library, filePath, _) in _libraries) { + final libraryElement = library.element; + final topElements = libraryElement.topLevelElements; + final classElements = topElements.whereType().where( + (e) => e.supertype?.element.library.identifier == yaroormIdentifier); + if (classElements.isEmpty) return; + + analyzeClassElement(libraryElement, filePath, classElements.first); + } + } + + void analyzeClassElement( + LibraryElement libElement, + String filePath, + ClassElement classElement, + ) async { + final fields = classElement.fields.where(allowedTypes).toList(); + + final className = classElement.name; + + final tableMeta = classElement.metadata + .firstWhereOrNull( + (e) => e.element?.library?.identifier == yaroormIdentifier) + ?.computeConstantValue(); + final tableName = tableMeta?.getField('name')?.toStringValue() ?? + className.snakeCase.toPlural().first; + + final primaryKey = getEntityField(fields, 'PrimaryKey'); + final createdAtField = getEntityField(fields, 'CreatedAtColumn')?.field; + final updatedAtField = getEntityField(fields, 'UpdatedAtColumn')?.field; + final converters = tableMeta?.getField('converters')!.toListValue(); + + if (primaryKey == null) { + throw Exception("$className Entity doesn't have primary key"); + } + + final autoIncrementPrimaryKey = + primaryKey.meta.getField('autoIncrement')!.toBoolValue()!; + final timestampsEnabled = (createdAtField ?? updatedAtField) != null; + + /// other properties aside primarykey, updatedAt and createdAt + final normalFields = fields.where( + (e) => ![createdAtField, updatedAtField, primaryKey.field].contains(e)); + + final creatableFields = [ + if (!autoIncrementPrimaryKey) primaryKey.field, + ...normalFields + ]; + + final primaryConstructor = + classElement.constructors.firstWhereOrNull((e) => e.name == ""); + if (primaryConstructor == null) { + throw '$className Entity does not have a default constructor'; + } + + final fieldNames = fields.map((e) => e.name); + final notAllowedProps = + primaryConstructor.children.where((e) => !fieldNames.contains(e.name)); + if (notAllowedProps.isNotEmpty) { + throw Exception( + 'These props are not allowed in $className Entity default constructor: ${notAllowedProps.join(', ')}'); + } + + bool isNullable(DartType type) { + return libElement.typeSystem.isNullable(type); + } + + String fieldToString(FieldElement e) { + final symbol = '#${e.name}'; + + final requiredOpts = ''' + "${e.name}", + ${e.type.getDisplayString(withNullability: false)}, + $symbol + '''; + + if (e == primaryKey.field) { + return '''PrimaryKeyField( + $requiredOpts) + ${!autoIncrementPrimaryKey ? ', autoIncrement: false' : ''}'''; + } + + return '''DBEntityField( + $requiredOpts + ${isNullable(e.type) ? ', nullable: true' : ''})''' + .trim(); + } + + final typeDataName = '${className.snakeCase}TypeData'; + final queryName = '${className}Query'; + + final library = Library((b) => b + ..comments.add('ignore_for_file: non_constant_identifier_names') + ..body.addAll([ + Directive.partOf(path.basename(filePath)), + Method((m) => m + ..name = queryName + ..returns = refer('Query<$className>') + ..type = MethodType.getter + ..lambda = true + ..body = Code('DB.query<$className>()')), + Method((m) => m + ..name = '${classElement.name.pascalCase}Schema' + ..returns = refer('CreateSchema') + ..lambda = true + ..type = MethodType.getter + ..body = Code('Schema.fromEntity<$className>()')), + Method((m) => m + ..name = typeDataName + ..returns = refer('DBEntity<$className>') + ..type = MethodType.getter + ..lambda = true + ..body = Code( + '''DBEntity<$className>( + "$tableName", + timestampsEnabled: $timestampsEnabled, + columns: ${fields.map(fieldToString).toList()}, + mirror: _\$${className}EntityMirror.new, + build: (args) => ${_generateConstructorCode(className, primaryConstructor)}, + ${converters == null ? '' : 'converters: ${converters.map(processAnnotation).toList()},'})''', + )), + Class( + (b) => b + ..name = '_\$${className}EntityMirror' + ..extend = refer('EntityMirror<$className>') + ..constructors.add(Constructor( + (b) => b + ..constant = true + ..requiredParameters.add(Parameter((p) => p + ..toSuper = true + ..name = 'instance')), + )) + ..methods.addAll([ + Method((m) => m + ..name = 'get' + ..annotations.add(CodeExpression(Code('override'))) + ..requiredParameters.add(Parameter((p) => p + ..name = 'field' + ..type = refer('Symbol'))) + ..returns = refer('Object?') + ..body = Code(''' +return switch(field) { + ${fields.map((e) => ''' + #${e.name} => instance.${e.name} +''').join(',')}, + _ => throw Exception('Unknown property \$field'), +}; +''')), + ]), + ), + Extension((b) => b + ..name = '${className}QueryExtension' + ..on = refer('Query<$className>') + ..methods.addAll([ + Method( + (m) => m + ..name = 'create' + ..returns = refer('Future<$className>') + ..modifier = MethodModifier.async + ..optionalParameters.addAll(creatableFields.map( + (field) => Parameter((p) => p + ..name = field.name + ..named = true + ..type = refer('${field.type}') + ..required = !isNullable(field.type)), + )) + ..body = Code('''return $queryName.insert({ + ${creatableFields.map((e) => '#${e.name}: ${e.name}').join(',')} + ,});'''), + ), + Method( + (m) => m + ..name = 'update' + ..returns = refer('Future') + ..modifier = MethodModifier.async + ..optionalParameters.addAll([ + Parameter((p) => p + ..name = 'where' + ..named = true + ..type = refer('WhereBuilder<$className>') + ..required = true), + Parameter((p) => p + ..name = 'value' + ..named = true + ..type = refer(className) + ..required = true) + ]) + ..body = Code(''' + final mirror = $typeDataName.mirror(value); + final props = { + for (final column in $typeDataName.columns) column.dartName: mirror.get(column.dartName), + }; + + final update = UpdateQuery( + entity.tableName, + whereClause: where(this), + data: conformToDbTypes(props, converters), + ); + + await $queryName.accept(update); +'''), + ), + ])) + ])); + + final actualFilePath = + path.basename(filePath).replaceFirst('.dart', '.entity.dart'); + + final df = path.join(path.dirname(filePath), actualFilePath); + + final result = DartFormatter().format('${library.accept(DartEmitter())}'); + File(df).writeAsStringSync(result); + } + + Stream<(ResolvedLibraryResult, String, String)> get _libraries async* { + for (var context in collection.contexts) { + final analyzedDartFiles = context.contextRoot.analyzedFiles().where( + (path) => + path.endsWith('.dart') && + !path.endsWith('_test.dart') && + !path.endsWith('.g.dart') && + !path.endsWith('.e.dart')); + + for (var filePath in analyzedDartFiles) { + var library = await context.currentSession.getResolvedLibrary(filePath); + if (library is ResolvedLibraryResult) { + yield (library, filePath, context.contextRoot.root.path); + } + } + } + } +} + +bool allowedTypes(FieldElement field) { + return field.getter?.isSynthetic ?? false; +} + +String _generateConstructorCode( + String className, ConstructorElement constructor) { + final sb = StringBuffer()..write('$className('); + + final normalParams = constructor.type.normalParameterNames; + final namedParams = constructor.type.namedParameterTypes.keys; + + if (normalParams.isNotEmpty) { + sb + ..write(normalParams.map((name) => 'args[#$name]').join(',')) + ..write(','); + } + + if (namedParams.isNotEmpty) { + sb + ..writeln(namedParams.map((name) => '$name: args[#$name]').join(', ')) + ..write(','); + } + + return (sb..write(')')).toString(); +} + +/// Returns the field and it's meta value +({FieldElement field, DartObject meta})? getEntityField( + List fields, String type) { + ElementAnnotation? metaData; + + final field = fields.firstWhereOrNull((f) { + metaData = f.metadata.firstWhereOrNull((e) => + e.element?.library?.identifier == yaroormIdentifier && + (e.element is PropertyAccessorElement) && + (e.element as PropertyAccessorElement).returnType.toString() == type); + + return metaData != null; + }); + if (field == null) return null; + + return (field: field, meta: metaData!.computeConstantValue()!); +} + +/// Process entity annotation +String processAnnotation(DartObject constantValue) { + final classElement = constantValue.type!.element as ClassElement; + assert(classElement.supertype!.typeArguments.length == 2, + 'Should have two type arguments'); + + final variable = constantValue.variable; + if (variable != null) return variable.name; + + final custructor = classElement.constructors.first; + if (custructor.parameters.isEmpty) { + return '${classElement.name}()'; + } + + /// TODO(codekeyz): resolve constructor for TypeConverters + throw UnsupportedError('Parameters for TypeConverters not yet supported'); +} diff --git a/packages/yaroo_cli/pubspec.yaml b/packages/yaroo_cli/pubspec.yaml index f8c541e0..f018685e 100644 --- a/packages/yaroo_cli/pubspec.yaml +++ b/packages/yaroo_cli/pubspec.yaml @@ -17,6 +17,13 @@ dependencies: yaroorm: mason_logger: ^0.2.11 + analyzer: + code_builder: ^4.10.0 + dart_style: + source_gen: ^1.5.0 + grammer: ^1.0.3 + recase: ^4.1.0 + dev_dependencies: test: ^1.24.0 lints: ^3.0.0 diff --git a/packages/yaroorm/.gitignore b/packages/yaroorm/.gitignore index 9850650e..77483a44 100644 --- a/packages/yaroorm/.gitignore +++ b/packages/yaroorm/.gitignore @@ -5,4 +5,5 @@ # Avoid committing pubspec.lock for library packages; see # https://dart.dev/guides/libraries/private-files#pubspeclock. pubspec.lock -*.sqlite \ No newline at end of file +*.sqlite +*.entity.dart \ No newline at end of file diff --git a/packages/yaroorm/lib/migration.dart b/packages/yaroorm/lib/migration.dart index 14c64eeb..366ca487 100644 --- a/packages/yaroorm/lib/migration.dart +++ b/packages/yaroorm/lib/migration.dart @@ -4,6 +4,8 @@ import 'package:meta/meta.dart'; import 'package:recase/recase.dart'; import 'package:yaroorm/yaroorm.dart'; +import 'src/reflection.dart'; + abstract class TableBlueprint { void id({String name = 'id', String? type, bool autoIncrement = true}); diff --git a/packages/yaroorm/lib/src/database/entity/entity.dart b/packages/yaroorm/lib/src/database/entity/entity.dart index 34d787b2..a9186d49 100644 --- a/packages/yaroorm/lib/src/database/entity/entity.dart +++ b/packages/yaroorm/lib/src/database/entity/entity.dart @@ -1,8 +1,10 @@ import 'package:recase/recase.dart'; -import 'package:yaroorm/yaroorm.dart'; import 'package:meta/meta_meta.dart'; +import '../../query/query.dart'; +import '../../reflection.dart'; + part 'converter.dart'; // part 'relations.dart'; diff --git a/packages/yaroorm/lib/src/primitives/_where_impl.dart b/packages/yaroorm/lib/src/primitives/_where_impl.dart index d7f7ff54..af3ca042 100644 --- a/packages/yaroorm/lib/src/primitives/_where_impl.dart +++ b/packages/yaroorm/lib/src/primitives/_where_impl.dart @@ -111,13 +111,6 @@ class _WhereClauseImpl extends WhereClause { return this; } - @override - Future update(Map values) { - return UpdateQuery(query.tableName, whereClause: this, data: values) - .driver(query.runner) - .execute(); - } - @override Future delete() { return DeleteQuery(query.tableName, whereClause: this) diff --git a/packages/yaroorm/lib/src/query/query.dart b/packages/yaroorm/lib/src/query/query.dart index 6a3f9a62..ae35501f 100644 --- a/packages/yaroorm/lib/src/query/query.dart +++ b/packages/yaroorm/lib/src/query/query.dart @@ -3,6 +3,7 @@ import 'package:yaroorm/src/query/aggregates.dart'; import 'package:yaroorm/yaroorm.dart'; import '../primitives/where.dart'; +import '../reflection.dart'; part 'query_impl.dart'; diff --git a/packages/yaroorm/lib/src/query/query_impl.dart b/packages/yaroorm/lib/src/query/query_impl.dart index 3d230a4b..35b49e98 100644 --- a/packages/yaroorm/lib/src/query/query_impl.dart +++ b/packages/yaroorm/lib/src/query/query_impl.dart @@ -11,7 +11,8 @@ class QueryImpl extends Query { String condition, [ Value? value, ]) { - final newClause = WhereClause.create(this, value: WhereClauseValue.from(field, condition, value)); + final newClause = WhereClause.create(this, + value: WhereClauseValue.from(field, condition, value)); whereClauses.add(newClause); return newClause; } @@ -31,7 +32,8 @@ class QueryImpl extends Query { @override Future insert(Map data) async { final dataToDbD = conformToDbTypes(data, converters); - final recordId = await runner.insert(InsertQuery(tableName, data: dataToDbD)); + final recordId = + await runner.insert(InsertQuery(tableName, data: dataToDbD)); return (await get(recordId))!; } @@ -73,8 +75,10 @@ class QueryImpl extends Query { } @override - WhereClause orWhere(String field, String condition, [Value? value]) { - throw StateError('Cannot use `orWhere` directly on a Query you need a WHERE clause first'); + WhereClause orWhere(String field, String condition, + [Value? value]) { + throw StateError( + 'Cannot use `orWhere` directly on a Query you need a WHERE clause first'); } @override @@ -86,7 +90,8 @@ class QueryImpl extends Query { final newQuery = QueryImpl(tableName); builder(newQuery); - final newGroup = WhereClause.create(this, operator: LogicalOperator.OR); + final newGroup = + WhereClause.create(this, operator: LogicalOperator.OR); for (final clause in newQuery.whereClauses) { newGroup.children.add((clause.operator, clause)); } @@ -101,7 +106,8 @@ class QueryImpl extends Query { final newQuery = QueryImpl(tableName); builder(newQuery); - final newGroup = WhereClause.create(this, operator: LogicalOperator.AND); + final newGroup = + WhereClause.create(this, operator: LogicalOperator.AND); for (final clause in newQuery.whereClauses) { newGroup.children.add((clause.operator, clause)); } @@ -113,72 +119,79 @@ class QueryImpl extends Query { @override WhereClause equal(String field, Value value) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.EQUAL, value: value))); + final newClause = WhereClause.create(this, + value: + WhereClauseValue(field, (operator: Operator.EQUAL, value: value))); whereClauses.add(newClause); return newClause; } @override WhereClause notEqual(String field, Value value) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NOT_EQUAL, value: value))); + final newClause = WhereClause.create(this, + value: WhereClauseValue( + field, (operator: Operator.NOT_EQUAL, value: value))); whereClauses.add(newClause); return newClause; } @override WhereClause isIn(String field, List values) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.IN, value: values))); + final newClause = WhereClause.create(this, + value: WhereClauseValue(field, (operator: Operator.IN, value: values))); whereClauses.add(newClause); return newClause; } @override WhereClause isNotIn(String field, List values) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NOT_IN, value: values))); + final newClause = WhereClause.create(this, + value: WhereClauseValue( + field, (operator: Operator.NOT_IN, value: values))); whereClauses.add(newClause); return newClause; } @override WhereClause isLike(String field, String pattern) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.LIKE, value: pattern))); + final newClause = WhereClause.create(this, + value: + WhereClauseValue(field, (operator: Operator.LIKE, value: pattern))); whereClauses.add(newClause); return newClause; } @override WhereClause isNotLike(String field, String pattern) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NOT_LIKE, value: pattern))); + final newClause = WhereClause.create(this, + value: WhereClauseValue( + field, (operator: Operator.NOT_LIKE, value: pattern))); whereClauses.add(newClause); return newClause; } @override WhereClause isNull(String field) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NULL, value: null))); + final newClause = WhereClause.create(this, + value: WhereClauseValue(field, (operator: Operator.NULL, value: null))); whereClauses.add(newClause); return newClause; } @override WhereClause isNotNull(String field) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NOT_NULL, value: null))); + final newClause = WhereClause.create(this, + value: WhereClauseValue( + field, (operator: Operator.NOT_NULL, value: null))); whereClauses.add(newClause); return newClause; } @override WhereClause isBetween(String field, List values) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.BETWEEN, value: values))); + final newClause = WhereClause.create(this, + value: WhereClauseValue( + field, (operator: Operator.BETWEEN, value: values))); whereClauses.add(newClause); return newClause; } @@ -186,7 +199,8 @@ class QueryImpl extends Query { @override WhereClause isNotBetween(String field, List values) { final newClause = WhereClause.create(this, - value: WhereClauseValue(field, (operator: Operator.NOT_BETWEEN, value: values))); + value: WhereClauseValue( + field, (operator: Operator.NOT_BETWEEN, value: values))); whereClauses.add(newClause); return newClause; } diff --git a/packages/yaroorm/lib/src/reflection/reflector.dart b/packages/yaroorm/lib/src/reflection.dart similarity index 97% rename from packages/yaroorm/lib/src/reflection/reflector.dart rename to packages/yaroorm/lib/src/reflection.dart index 2b3661c5..109746f6 100644 --- a/packages/yaroorm/lib/src/reflection/reflector.dart +++ b/packages/yaroorm/lib/src/reflection.dart @@ -16,7 +16,7 @@ abstract class EntityMirror { Object? get(Symbol field); } -class DBEntity { +final class DBEntity { Type get dartType => T; final String tableName; diff --git a/packages/yaroorm/lib/yaroorm.dart b/packages/yaroorm/lib/yaroorm.dart index 39af14a4..5aa63fda 100644 --- a/packages/yaroorm/lib/yaroorm.dart +++ b/packages/yaroorm/lib/yaroorm.dart @@ -2,7 +2,6 @@ library; export 'src/query/query.dart'; export 'src/primitives/where.dart' show WhereBuilder; -export 'src/reflection/reflector.dart'; export 'src/database/driver/driver.dart'; export 'src/database/entity/entity.dart'; export 'src/database/database.dart'; diff --git a/packages/yaroorm/pubspec.yaml b/packages/yaroorm/pubspec.yaml index 1b13568e..7f554b7b 100644 --- a/packages/yaroorm/pubspec.yaml +++ b/packages/yaroorm/pubspec.yaml @@ -31,3 +31,6 @@ dev_dependencies: yaroo_cli: path: ../yaroo_cli path: ^1.9.0 + build_runner: + copy_with_extension: ^5.0.4 + copy_with_extension_gen: ^5.0.4 diff --git a/packages/yaroorm/test/integration/e2e_basic.dart b/packages/yaroorm/test/integration/e2e_basic.dart index e2817e0c..ea12e07b 100644 --- a/packages/yaroorm/test/integration/e2e_basic.dart +++ b/packages/yaroorm/test/integration/e2e_basic.dart @@ -91,14 +91,12 @@ void runBasicE2ETest(String connectionName) { test('should update user', () async { final user = await db.query().get(); - expect(user!.id!, 1); + expect(user!.id, 1); - user - ..firstname = 'Red Oil' - ..age = 100; - await user.save(); + final updatedUser = + await user.copyWith(firstname: 'Red Oil', age: 100).save(); - final userFromDB = await db.query().get(user.id!); + final userFromDB = await db.query().get(user.id); expect(user, isNotNull); expect(userFromDB?.firstname, 'Red Oil'); expect(userFromDB?.age, 100); diff --git a/packages/yaroorm/test/integration/e2e_relation.dart b/packages/yaroorm/test/integration/e2e_relation.dart index 070562a9..e5bef96e 100644 --- a/packages/yaroorm/test/integration/e2e_relation.dart +++ b/packages/yaroorm/test/integration/e2e_relation.dart @@ -1,168 +1,168 @@ -import 'package:test/test.dart'; -import 'package:yaroorm/yaroorm.dart'; - -import 'fixtures/migrator.dart'; -import 'fixtures/test_data.dart'; - -void runRelationsE2ETest(String connectionName) { - final driver = DB.driver(connectionName); - - final tables = [User, Post, PostComment].map((e) => getEntityTableName(e)); - - test('tables names', - () => expect(tables, ['users', 'posts', 'post_comments'])); - - return group('with ${driver.type.name} driver', () { - User? testUser1, anotherUser; - - setUpAll(() async { - await driver.connect(); - - expect(driver.isOpen, isTrue); - - var hasTables = await Future.wait(tables.map(driver.hasTable)); - expect(hasTables.every((e) => e), isFalse); - - await runMigrator(connectionName, 'migrate'); - - hasTables = await Future.wait(tables.map(driver.hasTable)); - expect(hasTables.every((e) => e), isTrue); - - testUser1 = await User( - firstname: 'Baba', - lastname: 'Tunde', - age: 29, - homeAddress: "Owerri, Nigeria") - .withDriver(driver) - .save(); - expect( - testUser1, isA().having((p0) => p0.id, 'has primary key', 1)); - }); - - test('should add many posts for User', () async { - final postsToAdd = [ - Post('Foo Bar 1', 'foo bar 4'), - Post('Mee Moo 2', 'foo bar 5'), - Post('Coo Kie 3', 'foo bar 6'), - ]; - - await testUser1!.posts.addAll(postsToAdd); - - final posts = await testUser1!.posts.orderByAsc('title').get(); - expect(posts, hasLength(3)); - expect( - posts.every((e) => - e.createdAt != null && - e.updatedAt != null && - e.userId == testUser1!.id), - isTrue); - expect( - posts.map((e) => { - 'id': e.id, - 'title': e.title, - 'desc': e.description, - 'userId': e.userId! - }), - [ - {'id': 3, 'title': 'Coo Kie 3', 'desc': 'foo bar 6', 'userId': 1}, - {'id': 1, 'title': 'Foo Bar 1', 'desc': 'foo bar 4', 'userId': 1}, - {'id': 2, 'title': 'Mee Moo 2', 'desc': 'foo bar 5', 'userId': 1}, - ]); - }); - - test('should add comments for post', () async { - final post = await testUser1!.posts.first(); - expect(post, isA()); - - var comments = await post!.comments.get(); - expect(comments, isEmpty); - - final c = PostComment('this post looks abit old') - ..id = 'some_random_uuid_32893782738'; - await post.comments.add(c); - - comments = await post.comments.get(); - expect( - comments.map( - (e) => {'id': c.id, 'comment': c.comment, 'postId': e.postId}), - [ - { - 'id': 'some_random_uuid_32893782738', - 'comment': 'this post looks abit old', - 'postId': 1 - } - ]); - - await post.comments.add(PostComment('oh, another comment') - ..id = 'jagaban_299488474773_uuid_3i848'); - comments = await post.comments.orderByDesc('comment').get(); - expect(comments, hasLength(2)); - expect( - comments.map( - (e) => {'id': e.id, 'comment': e.comment, 'postId': e.postId}), - [ - { - 'id': 'some_random_uuid_32893782738', - 'comment': 'this post looks abit old', - 'postId': 1 - }, - { - 'id': 'jagaban_299488474773_uuid_3i848', - 'comment': 'oh, another comment', - 'postId': 1 - } - ]); - }); - - test('should add post for another user', () async { - anotherUser = await usersList.last.withDriver(driver).save(); - final user = anotherUser as User; - - expect(user.id, isNotNull); - expect(user.id != testUser1!.id, isTrue); - - var anotherUserPosts = await user.posts.get(); - expect(anotherUserPosts, isEmpty); - - await user.posts.add(Post('Another Post', 'wham bamn')); - anotherUserPosts = await user.posts.get(); - expect(anotherUserPosts, hasLength(1)); - - final anotherUserPost = anotherUserPosts.first; - expect(anotherUserPost.userId!, anotherUser!.id!); - - await anotherUserPost.comments.addAll([ - PostComment('ah ah')..id = '_id_394', - PostComment('oh oh')..id = '_id_394885', - ]); - - expect(await anotherUserPost.comments.get(), hasLength(2)); - }); - - test('should delete comments for post', () async { - expect(testUser1, isNotNull); - final posts = await testUser1!.posts.get(); - expect(posts, hasLength(3)); - - // ignore: curly_braces_in_flow_control_structures - for (final post in posts) await post.comments.delete(); - - expect( - await Future.wait(posts.map((e) => e.comments.get())), [[], [], []]); - - await testUser1!.posts.delete(); - - expect(await testUser1!.posts.get(), isEmpty); - }); - - tearDownAll(() async { - await runMigrator(connectionName, 'migrate:reset'); - - final hasTables = await Future.wait(tables.map(driver.hasTable)); - expect(hasTables.every((e) => e), isFalse); - - await driver.disconnect(); - expect(driver.isOpen, isFalse); - }); - }); -} +// import 'package:test/test.dart'; +// import 'package:yaroorm/yaroorm.dart'; + +// import 'fixtures/migrator.dart'; +// import 'fixtures/test_data.dart'; + +// void runRelationsE2ETest(String connectionName) { +// final driver = DB.driver(connectionName); + +// final tables = [User, Post, PostComment].map((e) => getEntityTableName(e)); + +// test('tables names', +// () => expect(tables, ['users', 'posts', 'post_comments'])); + +// return group('with ${driver.type.name} driver', () { +// User? testUser1, anotherUser; + +// setUpAll(() async { +// await driver.connect(); + +// expect(driver.isOpen, isTrue); + +// var hasTables = await Future.wait(tables.map(driver.hasTable)); +// expect(hasTables.every((e) => e), isFalse); + +// await runMigrator(connectionName, 'migrate'); + +// hasTables = await Future.wait(tables.map(driver.hasTable)); +// expect(hasTables.every((e) => e), isTrue); + +// testUser1 = await User( +// firstname: 'Baba', +// lastname: 'Tunde', +// age: 29, +// homeAddress: "Owerri, Nigeria") +// .withDriver(driver) +// .save(); +// expect( +// testUser1, isA().having((p0) => p0.id, 'has primary key', 1)); +// }); + +// test('should add many posts for User', () async { +// final postsToAdd = [ +// Post('Foo Bar 1', 'foo bar 4'), +// Post('Mee Moo 2', 'foo bar 5'), +// Post('Coo Kie 3', 'foo bar 6'), +// ]; + +// await testUser1!.posts.addAll(postsToAdd); + +// final posts = await testUser1!.posts.orderByAsc('title').get(); +// expect(posts, hasLength(3)); +// expect( +// posts.every((e) => +// e.createdAt != null && +// e.updatedAt != null && +// e.userId == testUser1!.id), +// isTrue); +// expect( +// posts.map((e) => { +// 'id': e.id, +// 'title': e.title, +// 'desc': e.description, +// 'userId': e.userId! +// }), +// [ +// {'id': 3, 'title': 'Coo Kie 3', 'desc': 'foo bar 6', 'userId': 1}, +// {'id': 1, 'title': 'Foo Bar 1', 'desc': 'foo bar 4', 'userId': 1}, +// {'id': 2, 'title': 'Mee Moo 2', 'desc': 'foo bar 5', 'userId': 1}, +// ]); +// }); + +// test('should add comments for post', () async { +// final post = await testUser1!.posts.first(); +// expect(post, isA()); + +// var comments = await post!.comments.get(); +// expect(comments, isEmpty); + +// final c = PostComment('this post looks abit old') +// ..id = 'some_random_uuid_32893782738'; +// await post.comments.add(c); + +// comments = await post.comments.get(); +// expect( +// comments.map( +// (e) => {'id': c.id, 'comment': c.comment, 'postId': e.postId}), +// [ +// { +// 'id': 'some_random_uuid_32893782738', +// 'comment': 'this post looks abit old', +// 'postId': 1 +// } +// ]); + +// await post.comments.add(PostComment('oh, another comment') +// ..id = 'jagaban_299488474773_uuid_3i848'); +// comments = await post.comments.orderByDesc('comment').get(); +// expect(comments, hasLength(2)); +// expect( +// comments.map( +// (e) => {'id': e.id, 'comment': e.comment, 'postId': e.postId}), +// [ +// { +// 'id': 'some_random_uuid_32893782738', +// 'comment': 'this post looks abit old', +// 'postId': 1 +// }, +// { +// 'id': 'jagaban_299488474773_uuid_3i848', +// 'comment': 'oh, another comment', +// 'postId': 1 +// } +// ]); +// }); + +// test('should add post for another user', () async { +// anotherUser = await usersList.last.withDriver(driver).save(); +// final user = anotherUser as User; + +// expect(user.id, isNotNull); +// expect(user.id != testUser1!.id, isTrue); + +// var anotherUserPosts = await user.posts.get(); +// expect(anotherUserPosts, isEmpty); + +// await user.posts.add(Post('Another Post', 'wham bamn')); +// anotherUserPosts = await user.posts.get(); +// expect(anotherUserPosts, hasLength(1)); + +// final anotherUserPost = anotherUserPosts.first; +// expect(anotherUserPost.userId!, anotherUser!.id!); + +// await anotherUserPost.comments.addAll([ +// PostComment('ah ah')..id = '_id_394', +// PostComment('oh oh')..id = '_id_394885', +// ]); + +// expect(await anotherUserPost.comments.get(), hasLength(2)); +// }); + +// test('should delete comments for post', () async { +// expect(testUser1, isNotNull); +// final posts = await testUser1!.posts.get(); +// expect(posts, hasLength(3)); + +// // ignore: curly_braces_in_flow_control_structures +// for (final post in posts) await post.comments.delete(); + +// expect( +// await Future.wait(posts.map((e) => e.comments.get())), [[], [], []]); + +// await testUser1!.posts.delete(); + +// expect(await testUser1!.posts.get(), isEmpty); +// }); + +// tearDownAll(() async { +// await runMigrator(connectionName, 'migrate:reset'); + +// final hasTables = await Future.wait(tables.map(driver.hasTable)); +// expect(hasTables.every((e) => e), isFalse); + +// await driver.disconnect(); +// expect(driver.isOpen, isFalse); +// }); +// }); +// } diff --git a/packages/yaroorm/test/integration/fixtures/migrations.dart b/packages/yaroorm/test/integration/fixtures/migrations.dart index 7dd8449e..88506959 100644 --- a/packages/yaroorm/test/integration/fixtures/migrations.dart +++ b/packages/yaroorm/test/integration/fixtures/migrations.dart @@ -25,7 +25,7 @@ class AddPostsTable extends Migration { ..integer('userId') ..string('title') ..string('description') - ..foreign( + ..foreign( onKey: (key) => key.actions( onUpdate: ForeignKeyAction.cascade, onDelete: ForeignKeyAction.cascade)) diff --git a/packages/yaroorm/test/integration/fixtures/test_data.dart b/packages/yaroorm/test/integration/fixtures/test_data.dart index fd2eb7c7..0c8342f1 100644 --- a/packages/yaroorm/test/integration/fixtures/test_data.dart +++ b/packages/yaroorm/test/integration/fixtures/test_data.dart @@ -1,217 +1,191 @@ -import 'package:yaroorm/yaroorm.dart'; +typedef UserData = ({ + String firstname, + String lastname, + int age, + String homeAddress +}); -class User extends Entity { - String firstname; - String lastname; - int age; - - @TableColumn(name: 'home_address') - String homeAddress; - - User({ - required this.firstname, - required this.lastname, - required this.age, - required this.homeAddress, - }); - - HasMany get posts => hasMany(); -} - -@Table(name: 'posts', enableTimestamps: true) -class Post extends Entity { - String title; - String description; - - final int? userId; - - Post(this.title, this.description, {this.userId}); - - HasMany get comments => hasMany(); -} - -@Table(name: 'post_comments', enableTimestamps: true) -class PostComment extends Entity { - final String comment; - final int? postId; - - PostComment(this.comment, {this.postId}); -} - -final usersList = [ +final usersList = [ /// Ghana Users - [6] - User( - firstname: 'Kofi', - lastname: 'Duke', - age: 22, - homeAddress: "Accra, Ghana"), - User( - firstname: 'Foo', lastname: 'Bar', age: 23, homeAddress: "Kumasi, Ghana"), - User( - firstname: 'Bar', - lastname: 'Moo', - age: 24, - homeAddress: "Cape Coast, Ghana"), - User(firstname: 'Kee', lastname: 'Koo', age: 25, homeAddress: "Accra, Ghana"), - User(firstname: 'Poo', lastname: 'Paa', age: 26, homeAddress: "Accra, Ghana"), - User( - firstname: 'Merh', - lastname: 'Moor', - age: 27, - homeAddress: "Accra, Ghana"), + (firstname: 'Kofi', lastname: 'Duke', age: 22, homeAddress: "Accra, Ghana"), + (firstname: 'Foo', lastname: 'Bar', age: 23, homeAddress: "Kumasi, Ghana"), + ( + firstname: 'Bar', + lastname: 'Moo', + age: 24, + homeAddress: "Cape Coast, Ghana" + ), + (firstname: 'Kee', lastname: 'Koo', age: 25, homeAddress: "Accra, Ghana"), + (firstname: 'Poo', lastname: 'Paa', age: 26, homeAddress: "Accra, Ghana"), + (firstname: 'Merh', lastname: 'Moor', age: 27, homeAddress: "Accra, Ghana"), /// Nigerian Users - [22] - User( - firstname: 'Abdul', - lastname: 'Ibrahim', - age: 28, - homeAddress: "Owerri, Nigeria"), - User( - firstname: 'Amina', - lastname: 'Sule', - age: 29, - homeAddress: "Owerri, Nigeria"), - User( - firstname: 'Chukwudi', - lastname: 'Okafor', - age: 30, - homeAddress: "Lagos, Nigeria"), - User( - firstname: 'Chioma', - lastname: 'Nwosu', - age: 31, - homeAddress: "Lagos, Nigeria"), - User( - firstname: 'Yusuf', - lastname: 'Aliyu', - age: 32, - homeAddress: "Owerri, Nigeria"), - User( - firstname: 'Blessing', - lastname: 'Okonkwo', - age: 33, - homeAddress: "Owerri, Nigeria"), - User( - firstname: 'Tunde', - lastname: 'Williams', - age: 34, - homeAddress: "Abuja, Nigeria"), - User( - firstname: 'Rukayat', - lastname: 'Sanni', - age: 35, - homeAddress: "Abuja, Nigeria"), - User( - firstname: 'Segun', - lastname: 'Adeleke', - age: 36, - homeAddress: "Lagos, Nigeria"), - User( - firstname: 'Abdullahi', - lastname: 'Mohammed', - age: 46, - homeAddress: "Lagos, Nigeria"), - User( - firstname: 'Chidinma', - lastname: 'Onyeka', - age: 47, - homeAddress: "Owerri, Nigeria"), - User( - firstname: 'Bola', - lastname: 'Akinwumi', - age: 48, - homeAddress: "Owerri, Nigeria"), - User( - firstname: 'Haruna', - lastname: 'Bello', - age: 49, - homeAddress: "Lagos, Nigeria"), - User( - firstname: 'Habiba', - lastname: 'Yusuf', - age: 50, - homeAddress: "Owerri, Nigeria"), - User( - firstname: 'Tochukwu', - lastname: 'Eze', - age: 50, - homeAddress: "Lagos, Nigeria"), - User( - firstname: 'Ade', - lastname: 'Ogunbanjo', - age: 50, - homeAddress: "Owerri, Nigeria"), - User( - firstname: 'Zainab', - lastname: 'Abubakar', - age: 50, - homeAddress: "Lagos, Nigeria"), - User( - firstname: 'Chijioke', - lastname: 'Nwachukwu', - age: 54, - homeAddress: "Owerri, Nigeria"), - User( - firstname: 'Folake', - lastname: 'Adewale', - age: 55, - homeAddress: "Owerri, Nigeria"), - User( - firstname: 'Mustafa', - lastname: 'Olawale', - age: 56, - homeAddress: "Lagos, Nigeria"), - User( - firstname: 'Halima', - lastname: 'Idris', - age: 57, - homeAddress: "Lagos, Nigeria"), - User( - firstname: 'Chukwuemeka', - lastname: 'Okonkwo', - age: 58, - homeAddress: "Abuja, Nigeria"), + ( + firstname: 'Abdul', + lastname: 'Ibrahim', + age: 28, + homeAddress: "Owerri, Nigeria" + ), + ( + firstname: 'Amina', + lastname: 'Sule', + age: 29, + homeAddress: "Owerri, Nigeria" + ), + ( + firstname: 'Chukwudi', + lastname: 'Okafor', + age: 30, + homeAddress: "Lagos, Nigeria" + ), + ( + firstname: 'Chioma', + lastname: 'Nwosu', + age: 31, + homeAddress: "Lagos, Nigeria" + ), + ( + firstname: 'Yusuf', + lastname: 'Aliyu', + age: 32, + homeAddress: "Owerri, Nigeria" + ), + ( + firstname: 'Blessing', + lastname: 'Okonkwo', + age: 33, + homeAddress: "Owerri, Nigeria" + ), + ( + firstname: 'Tunde', + lastname: 'Williams', + age: 34, + homeAddress: "Abuja, Nigeria" + ), + ( + firstname: 'Rukayat', + lastname: 'Sanni', + age: 35, + homeAddress: "Abuja, Nigeria" + ), + ( + firstname: 'Segun', + lastname: 'Adeleke', + age: 36, + homeAddress: "Lagos, Nigeria" + ), + ( + firstname: 'Abdullahi', + lastname: 'Mohammed', + age: 46, + homeAddress: "Lagos, Nigeria" + ), + ( + firstname: 'Chidinma', + lastname: 'Onyeka', + age: 47, + homeAddress: "Owerri, Nigeria" + ), + ( + firstname: 'Bola', + lastname: 'Akinwumi', + age: 48, + homeAddress: "Owerri, Nigeria" + ), + ( + firstname: 'Haruna', + lastname: 'Bello', + age: 49, + homeAddress: "Lagos, Nigeria" + ), + ( + firstname: 'Habiba', + lastname: 'Yusuf', + age: 50, + homeAddress: "Owerri, Nigeria" + ), + ( + firstname: 'Tochukwu', + lastname: 'Eze', + age: 50, + homeAddress: "Lagos, Nigeria" + ), + ( + firstname: 'Ade', + lastname: 'Ogunbanjo', + age: 50, + homeAddress: "Owerri, Nigeria" + ), + ( + firstname: 'Zainab', + lastname: 'Abubakar', + age: 50, + homeAddress: "Lagos, Nigeria" + ), + ( + firstname: 'Chijioke', + lastname: 'Nwachukwu', + age: 54, + homeAddress: "Owerri, Nigeria" + ), + ( + firstname: 'Folake', + lastname: 'Adewale', + age: 55, + homeAddress: "Owerri, Nigeria" + ), + ( + firstname: 'Mustafa', + lastname: 'Olawale', + age: 56, + homeAddress: "Lagos, Nigeria" + ), + ( + firstname: 'Halima', + lastname: 'Idris', + age: 57, + homeAddress: "Lagos, Nigeria" + ), + ( + firstname: 'Chukwuemeka', + lastname: 'Okonkwo', + age: 58, + homeAddress: "Abuja, Nigeria" + ), /// Kenyan Users - [9] - User( - firstname: 'Kevin', - lastname: 'Luke', - age: 37, - homeAddress: "Nairobi, Kenya"), - User(firstname: 'Foop', lastname: 'Farr', age: 38, homeAddress: "CBD, Kenya"), - User( - firstname: 'Koin', - lastname: 'Karl', - age: 39, - homeAddress: "Mumbasa, Kenya"), - User( - firstname: 'Moo', - lastname: 'Maa', - age: 40, - homeAddress: "Westlands, Kenya"), - User( - firstname: 'Merh', - lastname: 'Merh', - age: 41, - homeAddress: "Nairobi, Kenya"), - User( - firstname: 'Ibrahim', - lastname: 'Bakare', - age: 42, - homeAddress: "Nairobi, Kenya"), - User( - firstname: 'Grace', - lastname: 'Adegoke', - age: 43, - homeAddress: "Nairobi, Kenya"), - User( - firstname: 'Ahmed', - lastname: 'Umar', - age: 44, - homeAddress: "Nairobi, Kenya"), - User( - firstname: 'Nneka', - lastname: 'Okoli', - age: 45, - homeAddress: "Nairobi, Kenya"), + ( + firstname: 'Kevin', + lastname: 'Luke', + age: 37, + homeAddress: "Nairobi, Kenya" + ), + (firstname: 'Foop', lastname: 'Farr', age: 38, homeAddress: "CBD, Kenya"), + (firstname: 'Koin', lastname: 'Karl', age: 39, homeAddress: "Mumbasa, Kenya"), + (firstname: 'Moo', lastname: 'Maa', age: 40, homeAddress: "Westlands, Kenya"), + (firstname: 'Merh', lastname: 'Merh', age: 41, homeAddress: "Nairobi, Kenya"), + ( + firstname: 'Ibrahim', + lastname: 'Bakare', + age: 42, + homeAddress: "Nairobi, Kenya" + ), + ( + firstname: 'Grace', + lastname: 'Adegoke', + age: 43, + homeAddress: "Nairobi, Kenya" + ), + ( + firstname: 'Ahmed', + lastname: 'Umar', + age: 44, + homeAddress: "Nairobi, Kenya" + ), + ( + firstname: 'Nneka', + lastname: 'Okoli', + age: 45, + homeAddress: "Nairobi, Kenya" + ), ]; diff --git a/packages/yaroorm/test/integration/mariadb.e2e.dart b/packages/yaroorm/test/integration/mariadb.e2e.dart index 03eee6e8..8044445c 100644 --- a/packages/yaroorm/test/integration/mariadb.e2e.dart +++ b/packages/yaroorm/test/integration/mariadb.e2e.dart @@ -3,17 +3,13 @@ import 'package:yaroorm/yaroorm.dart'; import 'fixtures/orm_config.dart' as db; import 'e2e_basic.dart'; -import 'e2e_relation.dart'; -import 'mariadb.e2e.reflectable.dart'; void main() async { - initializeReflectable(); - DB.init(db.config); group('MariaDB', () { group('Basic E2E Test', () => runBasicE2ETest('bar_mariadb')); - group('Relation E2E Test', () => runRelationsE2ETest('bar_mariadb')); + // group('Relation E2E Test', () => runRelationsE2ETest('bar_mariadb')); }); } diff --git a/packages/yaroorm/test/integration/mysql.e2e.dart b/packages/yaroorm/test/integration/mysql.e2e.dart index e3f6902a..5213d7a8 100644 --- a/packages/yaroorm/test/integration/mysql.e2e.dart +++ b/packages/yaroorm/test/integration/mysql.e2e.dart @@ -3,17 +3,13 @@ import 'package:yaroorm/yaroorm.dart'; import 'fixtures/orm_config.dart' as db; import 'e2e_basic.dart'; -import 'e2e_relation.dart'; -import 'mysql.e2e.reflectable.dart'; void main() async { - initializeReflectable(); - DB.init(db.config); group('MySQL', () { group('Basic E2E Test', () => runBasicE2ETest('moo_mysql')); - group('Relation E2E Test', () => runRelationsE2ETest('moo_mysql')); + // group('Relation E2E Test', () => runRelationsE2ETest('moo_mysql')); }); } diff --git a/packages/yaroorm/test/integration/pgsql.e2e.dart b/packages/yaroorm/test/integration/pgsql.e2e.dart index e135d7a4..75113291 100644 --- a/packages/yaroorm/test/integration/pgsql.e2e.dart +++ b/packages/yaroorm/test/integration/pgsql.e2e.dart @@ -2,17 +2,13 @@ import 'package:test/test.dart'; import 'package:yaroorm/yaroorm.dart'; import 'fixtures/orm_config.dart' as db; import 'e2e_basic.dart'; -import 'e2e_relation.dart'; -import 'pgsql.e2e.reflectable.dart'; void main() async { - initializeReflectable(); - DB.init(db.config); group('Postgres', () { group('Basic E2E Test', () => runBasicE2ETest('foo_pgsql')); - group('Relation E2E Test', () => runRelationsE2ETest('foo_pgsql')); + // group('Relation E2E Test', () => runRelationsE2ETest('foo_pgsql')); }); } diff --git a/packages/yaroorm/test/integration/sqlite.e2e.dart b/packages/yaroorm/test/integration/sqlite.e2e.dart index 2d09ab02..5591bef6 100644 --- a/packages/yaroorm/test/integration/sqlite.e2e.dart +++ b/packages/yaroorm/test/integration/sqlite.e2e.dart @@ -3,18 +3,13 @@ import 'package:yaroorm/yaroorm.dart'; import 'fixtures/orm_config.dart' as db; import 'e2e_basic.dart'; -import 'e2e_relation.dart'; - -import 'sqlite.e2e.reflectable.dart'; void main() async { - initializeReflectable(); - DB.init(db.config); group('SQLite', () { group('Basic E2E Test', () => runBasicE2ETest('foo_sqlite')); - group('Relation E2E Test', () => runRelationsE2ETest('foo_sqlite')); + // group('Relation E2E Test', () => runRelationsE2ETest('foo_sqlite')); }); } diff --git a/packages/yaroorm/test/models/models.dart b/packages/yaroorm/test/models/models.dart new file mode 100644 index 00000000..e8f22d7e --- /dev/null +++ b/packages/yaroorm/test/models/models.dart @@ -0,0 +1,66 @@ +import 'package:copy_with_extension/copy_with_extension.dart'; +import 'package:yaroorm/yaroorm.dart'; +import 'package:yaroorm/src/reflection.dart'; + +part 'models.g.dart'; + +@CopyWith() +class User extends Entity { + @primaryKey + final int id; + + final String firstname; + final String lastname; + final int age; + + @TableColumn(name: 'home_address') + final String homeAddress; + + User({ + required this.id, + required this.firstname, + required this.lastname, + required this.age, + required this.homeAddress, + }); + + // HasMany get posts => hasMany(); +} + +// @CopyWith() +// @Table('posts') +// class Post extends Entity { +// @primaryKey +// final int id; + +// final String title; +// final String description; + +// final int userId; + +// @createdAtCol +// final DateTime createdAt; + +// @updatedAtCol +// final DateTime updatedAt; + +// Post( +// this.id, +// this.title, +// this.description, { +// required this.userId, +// required this.createdAt, +// required this.updatedAt, +// }); + +// // HasMany get comments => hasMany(); +// } + + +// @Table('post_comments', enableTimestamps: true) +// class PostComment extends Entity { +// final String comment; +// final int? postId; + +// PostComment(this.comment, {this.postId}); +// } From d2332d624a0490bc4f1c3eb59206a208b6e41cd5 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sat, 6 Apr 2024 11:29:39 +0000 Subject: [PATCH 13/50] resolve yaroo cli issues --- packages/yaroo_cli/lib/orm/_misc.dart | 12 +- .../yaroo_cli/lib/orm/commands/command.dart | 1 - .../orm/commands/migrate_reset_command.dart | 1 - .../commands/migrate_rollback_command.dart | 11 +- .../yaroo_cli/lib/src/command_runner.dart | 3 - .../yaroo_cli/lib/src/commands/generate.dart | 19 - packages/yaroo_cli/lib/src/migration.dart | 74 +--- packages/yaroo_cli/lib/src/type_analyzer.dart | 324 ------------------ packages/yaroo_cli/pubspec.yaml | 8 +- 9 files changed, 19 insertions(+), 434 deletions(-) delete mode 100644 packages/yaroo_cli/lib/src/commands/generate.dart delete mode 100644 packages/yaroo_cli/lib/src/type_analyzer.dart diff --git a/packages/yaroo_cli/lib/orm/_misc.dart b/packages/yaroo_cli/lib/orm/_misc.dart index 7442d3dd..e21c03c0 100644 --- a/packages/yaroo_cli/lib/orm/_misc.dart +++ b/packages/yaroo_cli/lib/orm/_misc.dart @@ -1,11 +1,14 @@ import 'package:yaroo_cli/src/migration.dart'; import 'package:yaroorm/yaroorm.dart'; +// ignore: non_constant_identifier_names +final MigrationQuery = DB.query(DB.config.migrationsTable); + Future ensureMigrationsTableReady(DatabaseDriver driver) async { final hasTable = await driver.hasTable(DB.config.migrationsTable); if (hasTable) return; - final script = MigrationDataSchema.toScript(driver.blueprint); + final script = MigrationEntitySchema.toScript(driver.blueprint); await driver.execute(script); } @@ -13,7 +16,9 @@ Future hasAlreadyMigratedScript( String scriptName, DatabaseDriver driver, ) async { - final result = await MigrationQuery.driver(driver).equal('migration', scriptName).findOne(); + final result = await MigrationQuery.driver(driver) + .equal('migration', scriptName) + .findOne(); return result != null; } @@ -21,5 +26,6 @@ Future getLastBatchNumber( DatabaseDriver driver, String migrationsTable, ) async { - return (await MigrationQuery.driver(driver).max('batch')).toInt(); + final result = await MigrationQuery.driver(driver).max('batch'); + return result.toInt(); } diff --git a/packages/yaroo_cli/lib/orm/commands/command.dart b/packages/yaroo_cli/lib/orm/commands/command.dart index 8afb78b1..183247b8 100644 --- a/packages/yaroo_cli/lib/orm/commands/command.dart +++ b/packages/yaroo_cli/lib/orm/commands/command.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:args/command_runner.dart'; -import 'package:yaroorm/migration.dart'; import 'package:yaroorm/yaroorm.dart'; class MigrationDefn { diff --git a/packages/yaroo_cli/lib/orm/commands/migrate_reset_command.dart b/packages/yaroo_cli/lib/orm/commands/migrate_reset_command.dart index ff4c1c05..b0688e85 100644 --- a/packages/yaroo_cli/lib/orm/commands/migrate_reset_command.dart +++ b/packages/yaroo_cli/lib/orm/commands/migrate_reset_command.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:collection/collection.dart'; -import 'package:yaroo_cli/src/migration.dart'; import 'package:yaroorm/yaroorm.dart'; import '../_misc.dart'; diff --git a/packages/yaroo_cli/lib/orm/commands/migrate_rollback_command.dart b/packages/yaroo_cli/lib/orm/commands/migrate_rollback_command.dart index 45a816a3..5f75da82 100644 --- a/packages/yaroo_cli/lib/orm/commands/migrate_rollback_command.dart +++ b/packages/yaroo_cli/lib/orm/commands/migrate_rollback_command.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:yaroo_cli/src/migration.dart'; -import 'package:yaroorm/migration.dart'; import 'package:yaroorm/yaroorm.dart'; import '../_misc.dart'; @@ -22,7 +21,7 @@ class MigrationRollbackCommand extends OrmCommand { final lastBatchNumber = await getLastBatchNumber(driver, migrationTableName); - final entries = await MigrationQuery.driver(driver) + final entries = await MigrationEntityQuery.driver(driver) .equal('batch', lastBatchNumber) .findMany(); @@ -50,7 +49,7 @@ class MigrationRollbackCommand extends OrmCommand { } } -typedef Rollback = ({MigrationData entry, List schemas}); +typedef Rollback = ({MigrationEntity entry, List schemas}); Future processRollbacks( DatabaseDriver driver, @@ -62,9 +61,9 @@ Future processRollbacks( await transactor.execute(e.toScript(driver.blueprint)); } - await Query.table( - DB.config.migrationsTable, - ).driver(transactor).equal('id', rollback.entry.id).delete(); + await MigrationQuery.driver(transactor) + .equal('id', rollback.entry.id) + .delete(); }); print('✔ rolled back: ${rollback.entry.migration}'); diff --git a/packages/yaroo_cli/lib/src/command_runner.dart b/packages/yaroo_cli/lib/src/command_runner.dart index db5f57d4..e1842453 100644 --- a/packages/yaroo_cli/lib/src/command_runner.dart +++ b/packages/yaroo_cli/lib/src/command_runner.dart @@ -1,6 +1,5 @@ import 'package:cli_completion/cli_completion.dart'; import 'package:mason_logger/mason_logger.dart'; -import 'package:yaroo_cli/src/commands/generate.dart'; import 'package:yaroo_cli/src/logger.dart'; const executableName = 'yaroo'; @@ -17,8 +16,6 @@ class YarooCliCommandRunner extends CompletionCommandRunner { callback: (verbose) { if (verbose) logger.level = Level.verbose; }); - - addCommand(GenerateEntityCommand()); } @override diff --git a/packages/yaroo_cli/lib/src/commands/generate.dart b/packages/yaroo_cli/lib/src/commands/generate.dart deleted file mode 100644 index f2efeef1..00000000 --- a/packages/yaroo_cli/lib/src/commands/generate.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:args/command_runner.dart'; -import 'package:yaroo_cli/src/type_analyzer.dart'; - -class GenerateEntityCommand extends Command { - @override - String get description => 'Generate entity related code'; - - @override - String get name => 'generate'; - - @override - Future run() async { - await EntityAnalyzer(Directory.current).analyze(); - return 0; - } -} diff --git a/packages/yaroo_cli/lib/src/migration.dart b/packages/yaroo_cli/lib/src/migration.dart index 3bc31f38..f3ee0511 100644 --- a/packages/yaroo_cli/lib/src/migration.dart +++ b/packages/yaroo_cli/lib/src/migration.dart @@ -1,12 +1,11 @@ // ignore_for_file: non_constant_identifier_names -import 'package:yaroorm/migration.dart'; import 'package:yaroorm/yaroorm.dart'; -// ignore: implementation_imports -import 'package:yaroorm/src/reflection.dart'; +part 'migration.g.dart'; -class MigrationData extends Entity { +@Table('migrations') +class MigrationEntity extends Entity { @primaryKey final int id; @@ -14,70 +13,5 @@ class MigrationData extends Entity { final int batch; - MigrationData(this.id, this.migration, this.batch); -} - -class _$MigrationDataEntityMirror extends EntityMirror { - const _$MigrationDataEntityMirror(super.instance); - - @override - Object? get(Symbol field) => switch (field) { - #id => instance.id, - #migration => instance.migration, - #batch => instance.batch, - _ => throw Exception('Unknown property $field'), - }; -} - -final _typeData = DBEntity( - "migrations", - timestampsEnabled: false, - columns: [ - PrimaryKeyField("id", int, #id), - DBEntityField("migration", String, #migration), - DBEntityField("batch", int, #batch) - ], - mirror: _$MigrationDataEntityMirror.new, - build: (args) => MigrationData( - args[#id], - args[#migration], - args[#batch], - ), -); - -extension MigrationDataQueryExtension on Query { - Future create({ - required String migration, - required int batch, - }) { - return insert({#migration: migration, #batch: batch}); - } - - Future update({ - required WhereBuilder where, - required MigrationData value, - }) async { - final props = { - for (final column in _typeData.columns) - column.dartName: _typeData.mirror(value).get(column.dartName), - }; - - final update = UpdateQuery( - entity.tableName, - whereClause: where(this), - data: conformToDbTypes(props, converters), - ); - - await accept(update); - } -} - -Query get MigrationQuery { - Query.addTypeDef(_typeData); - return DB.query(); -} - -CreateSchema get MigrationDataSchema { - Query.addTypeDef(_typeData); - return Schema.fromEntity(); + MigrationEntity(this.id, this.migration, this.batch); } diff --git a/packages/yaroo_cli/lib/src/type_analyzer.dart b/packages/yaroo_cli/lib/src/type_analyzer.dart deleted file mode 100644 index 99df770e..00000000 --- a/packages/yaroo_cli/lib/src/type_analyzer.dart +++ /dev/null @@ -1,324 +0,0 @@ -import 'dart:io'; - -import 'package:analyzer/dart/analysis/analysis_context_collection.dart'; -import 'package:analyzer/dart/analysis/results.dart'; -import 'package:analyzer/dart/constant/value.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/file_system/physical_file_system.dart'; -import 'package:code_builder/code_builder.dart'; -import 'package:dart_style/dart_style.dart'; -import 'package:path/path.dart' as path; -import 'package:recase/recase.dart'; -import 'package:grammer/grammer.dart'; -import 'package:collection/collection.dart'; - -const yaroormIdentifier = 'package:yaroorm/src/database/entity/entity.dart'; - -class EntityAnalyzer { - final AnalysisContextCollection collection; - - EntityAnalyzer(Directory projectDir) - : collection = AnalysisContextCollection( - includedPaths: [ - path.join(projectDir.path, 'lib/src/models'), - path.join(projectDir.path, 'test/models'), - ], - resourceProvider: PhysicalResourceProvider.INSTANCE, - ); - - Future analyze() async { - await for (var (library, filePath, _) in _libraries) { - final libraryElement = library.element; - final topElements = libraryElement.topLevelElements; - final classElements = topElements.whereType().where( - (e) => e.supertype?.element.library.identifier == yaroormIdentifier); - if (classElements.isEmpty) return; - - analyzeClassElement(libraryElement, filePath, classElements.first); - } - } - - void analyzeClassElement( - LibraryElement libElement, - String filePath, - ClassElement classElement, - ) async { - final fields = classElement.fields.where(allowedTypes).toList(); - - final className = classElement.name; - - final tableMeta = classElement.metadata - .firstWhereOrNull( - (e) => e.element?.library?.identifier == yaroormIdentifier) - ?.computeConstantValue(); - final tableName = tableMeta?.getField('name')?.toStringValue() ?? - className.snakeCase.toPlural().first; - - final primaryKey = getEntityField(fields, 'PrimaryKey'); - final createdAtField = getEntityField(fields, 'CreatedAtColumn')?.field; - final updatedAtField = getEntityField(fields, 'UpdatedAtColumn')?.field; - final converters = tableMeta?.getField('converters')!.toListValue(); - - if (primaryKey == null) { - throw Exception("$className Entity doesn't have primary key"); - } - - final autoIncrementPrimaryKey = - primaryKey.meta.getField('autoIncrement')!.toBoolValue()!; - final timestampsEnabled = (createdAtField ?? updatedAtField) != null; - - /// other properties aside primarykey, updatedAt and createdAt - final normalFields = fields.where( - (e) => ![createdAtField, updatedAtField, primaryKey.field].contains(e)); - - final creatableFields = [ - if (!autoIncrementPrimaryKey) primaryKey.field, - ...normalFields - ]; - - final primaryConstructor = - classElement.constructors.firstWhereOrNull((e) => e.name == ""); - if (primaryConstructor == null) { - throw '$className Entity does not have a default constructor'; - } - - final fieldNames = fields.map((e) => e.name); - final notAllowedProps = - primaryConstructor.children.where((e) => !fieldNames.contains(e.name)); - if (notAllowedProps.isNotEmpty) { - throw Exception( - 'These props are not allowed in $className Entity default constructor: ${notAllowedProps.join(', ')}'); - } - - bool isNullable(DartType type) { - return libElement.typeSystem.isNullable(type); - } - - String fieldToString(FieldElement e) { - final symbol = '#${e.name}'; - - final requiredOpts = ''' - "${e.name}", - ${e.type.getDisplayString(withNullability: false)}, - $symbol - '''; - - if (e == primaryKey.field) { - return '''PrimaryKeyField( - $requiredOpts) - ${!autoIncrementPrimaryKey ? ', autoIncrement: false' : ''}'''; - } - - return '''DBEntityField( - $requiredOpts - ${isNullable(e.type) ? ', nullable: true' : ''})''' - .trim(); - } - - final typeDataName = '${className.snakeCase}TypeData'; - final queryName = '${className}Query'; - - final library = Library((b) => b - ..comments.add('ignore_for_file: non_constant_identifier_names') - ..body.addAll([ - Directive.partOf(path.basename(filePath)), - Method((m) => m - ..name = queryName - ..returns = refer('Query<$className>') - ..type = MethodType.getter - ..lambda = true - ..body = Code('DB.query<$className>()')), - Method((m) => m - ..name = '${classElement.name.pascalCase}Schema' - ..returns = refer('CreateSchema') - ..lambda = true - ..type = MethodType.getter - ..body = Code('Schema.fromEntity<$className>()')), - Method((m) => m - ..name = typeDataName - ..returns = refer('DBEntity<$className>') - ..type = MethodType.getter - ..lambda = true - ..body = Code( - '''DBEntity<$className>( - "$tableName", - timestampsEnabled: $timestampsEnabled, - columns: ${fields.map(fieldToString).toList()}, - mirror: _\$${className}EntityMirror.new, - build: (args) => ${_generateConstructorCode(className, primaryConstructor)}, - ${converters == null ? '' : 'converters: ${converters.map(processAnnotation).toList()},'})''', - )), - Class( - (b) => b - ..name = '_\$${className}EntityMirror' - ..extend = refer('EntityMirror<$className>') - ..constructors.add(Constructor( - (b) => b - ..constant = true - ..requiredParameters.add(Parameter((p) => p - ..toSuper = true - ..name = 'instance')), - )) - ..methods.addAll([ - Method((m) => m - ..name = 'get' - ..annotations.add(CodeExpression(Code('override'))) - ..requiredParameters.add(Parameter((p) => p - ..name = 'field' - ..type = refer('Symbol'))) - ..returns = refer('Object?') - ..body = Code(''' -return switch(field) { - ${fields.map((e) => ''' - #${e.name} => instance.${e.name} -''').join(',')}, - _ => throw Exception('Unknown property \$field'), -}; -''')), - ]), - ), - Extension((b) => b - ..name = '${className}QueryExtension' - ..on = refer('Query<$className>') - ..methods.addAll([ - Method( - (m) => m - ..name = 'create' - ..returns = refer('Future<$className>') - ..modifier = MethodModifier.async - ..optionalParameters.addAll(creatableFields.map( - (field) => Parameter((p) => p - ..name = field.name - ..named = true - ..type = refer('${field.type}') - ..required = !isNullable(field.type)), - )) - ..body = Code('''return $queryName.insert({ - ${creatableFields.map((e) => '#${e.name}: ${e.name}').join(',')} - ,});'''), - ), - Method( - (m) => m - ..name = 'update' - ..returns = refer('Future') - ..modifier = MethodModifier.async - ..optionalParameters.addAll([ - Parameter((p) => p - ..name = 'where' - ..named = true - ..type = refer('WhereBuilder<$className>') - ..required = true), - Parameter((p) => p - ..name = 'value' - ..named = true - ..type = refer(className) - ..required = true) - ]) - ..body = Code(''' - final mirror = $typeDataName.mirror(value); - final props = { - for (final column in $typeDataName.columns) column.dartName: mirror.get(column.dartName), - }; - - final update = UpdateQuery( - entity.tableName, - whereClause: where(this), - data: conformToDbTypes(props, converters), - ); - - await $queryName.accept(update); -'''), - ), - ])) - ])); - - final actualFilePath = - path.basename(filePath).replaceFirst('.dart', '.entity.dart'); - - final df = path.join(path.dirname(filePath), actualFilePath); - - final result = DartFormatter().format('${library.accept(DartEmitter())}'); - File(df).writeAsStringSync(result); - } - - Stream<(ResolvedLibraryResult, String, String)> get _libraries async* { - for (var context in collection.contexts) { - final analyzedDartFiles = context.contextRoot.analyzedFiles().where( - (path) => - path.endsWith('.dart') && - !path.endsWith('_test.dart') && - !path.endsWith('.g.dart') && - !path.endsWith('.e.dart')); - - for (var filePath in analyzedDartFiles) { - var library = await context.currentSession.getResolvedLibrary(filePath); - if (library is ResolvedLibraryResult) { - yield (library, filePath, context.contextRoot.root.path); - } - } - } - } -} - -bool allowedTypes(FieldElement field) { - return field.getter?.isSynthetic ?? false; -} - -String _generateConstructorCode( - String className, ConstructorElement constructor) { - final sb = StringBuffer()..write('$className('); - - final normalParams = constructor.type.normalParameterNames; - final namedParams = constructor.type.namedParameterTypes.keys; - - if (normalParams.isNotEmpty) { - sb - ..write(normalParams.map((name) => 'args[#$name]').join(',')) - ..write(','); - } - - if (namedParams.isNotEmpty) { - sb - ..writeln(namedParams.map((name) => '$name: args[#$name]').join(', ')) - ..write(','); - } - - return (sb..write(')')).toString(); -} - -/// Returns the field and it's meta value -({FieldElement field, DartObject meta})? getEntityField( - List fields, String type) { - ElementAnnotation? metaData; - - final field = fields.firstWhereOrNull((f) { - metaData = f.metadata.firstWhereOrNull((e) => - e.element?.library?.identifier == yaroormIdentifier && - (e.element is PropertyAccessorElement) && - (e.element as PropertyAccessorElement).returnType.toString() == type); - - return metaData != null; - }); - if (field == null) return null; - - return (field: field, meta: metaData!.computeConstantValue()!); -} - -/// Process entity annotation -String processAnnotation(DartObject constantValue) { - final classElement = constantValue.type!.element as ClassElement; - assert(classElement.supertype!.typeArguments.length == 2, - 'Should have two type arguments'); - - final variable = constantValue.variable; - if (variable != null) return variable.name; - - final custructor = classElement.constructors.first; - if (custructor.parameters.isEmpty) { - return '${classElement.name}()'; - } - - /// TODO(codekeyz): resolve constructor for TypeConverters - throw UnsupportedError('Parameters for TypeConverters not yet supported'); -} diff --git a/packages/yaroo_cli/pubspec.yaml b/packages/yaroo_cli/pubspec.yaml index f018685e..a024f00e 100644 --- a/packages/yaroo_cli/pubspec.yaml +++ b/packages/yaroo_cli/pubspec.yaml @@ -17,16 +17,10 @@ dependencies: yaroorm: mason_logger: ^0.2.11 - analyzer: - code_builder: ^4.10.0 - dart_style: - source_gen: ^1.5.0 - grammer: ^1.0.3 - recase: ^4.1.0 - dev_dependencies: test: ^1.24.0 lints: ^3.0.0 + build_runner: executables: yaroo: From 5238039bbdc909ec15f2e7641e24e59e5b3da344 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sat, 6 Apr 2024 11:31:15 +0000 Subject: [PATCH 14/50] don't ignore migration generated file --- packages/yaroo_cli/.gitignore | 1 + packages/yaroo_cli/lib/src/migration.g.dart | 71 +++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 packages/yaroo_cli/lib/src/migration.g.dart diff --git a/packages/yaroo_cli/.gitignore b/packages/yaroo_cli/.gitignore index 3a857904..e0131e4e 100644 --- a/packages/yaroo_cli/.gitignore +++ b/packages/yaroo_cli/.gitignore @@ -1,3 +1,4 @@ # https://dart.dev/guides/libraries/private-files # Created by `dart pub` .dart_tool/ +!migration.g.dart \ No newline at end of file diff --git a/packages/yaroo_cli/lib/src/migration.g.dart b/packages/yaroo_cli/lib/src/migration.g.dart new file mode 100644 index 00000000..9fede7be --- /dev/null +++ b/packages/yaroo_cli/lib/src/migration.g.dart @@ -0,0 +1,71 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'migration.dart'; + +// ************************************************************************** +// YaroormGenerator +// ************************************************************************** + +// ignore_for_file: non_constant_identifier_names + +Query get MigrationEntityQuery => DB.query(); +CreateSchema get MigrationEntitySchema => Schema.fromEntity(); +DBEntity get migration_entityTypeData => + DBEntity( + "migrations", + timestampsEnabled: false, + columns: [ + PrimaryKeyField("id", int, #id), + DBEntityField("migration", String, #migration), + DBEntityField("batch", int, #batch) + ], + mirror: _$MigrationEntityEntityMirror.new, + build: (args) => MigrationEntity( + args[#id], + args[#migration], + args[#batch], + ), + ); + +class _$MigrationEntityEntityMirror extends EntityMirror { + const _$MigrationEntityEntityMirror(super.instance); + + @override + Object? get(Symbol field) { + return switch (field) { + #id => instance.id, + #migration => instance.migration, + #batch => instance.batch, + _ => throw Exception('Unknown property $field'), + }; + } +} + +extension MigrationEntityQueryExtension on Query { + Future create({ + required String migration, + required int batch, + }) => + insert({ + #migration: migration, + #batch: batch, + }); + Future update({ + required WhereBuilder where, + required MigrationEntity value, + }) async { + final mirror = migration_entityTypeData.mirror(value); + final props = { + for (final column in migration_entityTypeData.columns) + column.dartName: mirror.get(column.dartName), + }; + + final update = UpdateQuery( + entity.tableName, + whereClause: where(this), + data: conformToDbTypes(props, converters), + ); + + await accept(update); + } +} From f469cdbdb57c7a2d6067d406a52fb59551fddfc3 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sat, 6 Apr 2024 12:31:38 +0000 Subject: [PATCH 15/50] add build runner package for yaroorm --- packages/generator/.gitignore | 3 + packages/generator/CHANGELOG.md | 3 + packages/generator/README.md | 2 + packages/generator/analysis_options.yaml | 30 ++ packages/generator/build.yaml | 8 + packages/generator/lib/builder.dart | 7 + packages/generator/lib/src/generator.dart | 321 +++++++++++++++++ packages/generator/pubspec.yaml | 19 + packages/yaroorm/lib/src/config.dart | 2 +- .../lib/src/database/driver/driver.dart | 2 +- .../lib/src/database/driver/mysql_driver.dart | 2 +- .../lib/src/database/driver/pgsql_driver.dart | 2 +- .../src/database/driver/sqlite_driver.dart | 2 +- .../lib/src/database/entity/entity.dart | 9 +- packages/yaroorm/lib/{ => src}/migration.dart | 2 +- .../lib/src/primitives/serializer.dart | 2 +- packages/yaroorm/lib/src/utils.dart | 10 + packages/yaroorm/lib/yaroorm.dart | 3 + packages/yaroorm/pubspec.yaml | 7 +- .../yaroorm/test/integration/e2e_basic.dart | 249 +++++++------ .../test/integration/fixtures/migrations.dart | 42 +-- .../test/integration/fixtures/migrator.dart | 4 +- packages/yaroorm/test/models/models.dart | 62 ++-- .../test/unit/dialects/sqlite_test.dart | 339 +++++++++--------- 24 files changed, 767 insertions(+), 365 deletions(-) create mode 100644 packages/generator/.gitignore create mode 100644 packages/generator/CHANGELOG.md create mode 100644 packages/generator/README.md create mode 100644 packages/generator/analysis_options.yaml create mode 100644 packages/generator/build.yaml create mode 100644 packages/generator/lib/builder.dart create mode 100644 packages/generator/lib/src/generator.dart create mode 100644 packages/generator/pubspec.yaml rename packages/yaroorm/lib/{ => src}/migration.dart (99%) create mode 100644 packages/yaroorm/lib/src/utils.dart diff --git a/packages/generator/.gitignore b/packages/generator/.gitignore new file mode 100644 index 00000000..3a857904 --- /dev/null +++ b/packages/generator/.gitignore @@ -0,0 +1,3 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ diff --git a/packages/generator/CHANGELOG.md b/packages/generator/CHANGELOG.md new file mode 100644 index 00000000..effe43c8 --- /dev/null +++ b/packages/generator/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version. diff --git a/packages/generator/README.md b/packages/generator/README.md new file mode 100644 index 00000000..3816eca3 --- /dev/null +++ b/packages/generator/README.md @@ -0,0 +1,2 @@ +A sample command-line application with an entrypoint in `bin/`, library code +in `lib/`, and example unit test in `test/`. diff --git a/packages/generator/analysis_options.yaml b/packages/generator/analysis_options.yaml new file mode 100644 index 00000000..dee8927a --- /dev/null +++ b/packages/generator/analysis_options.yaml @@ -0,0 +1,30 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +# linter: +# rules: +# - camel_case_types + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/packages/generator/build.yaml b/packages/generator/build.yaml new file mode 100644 index 00000000..3fcce95f --- /dev/null +++ b/packages/generator/build.yaml @@ -0,0 +1,8 @@ +builders: + yaroorm_generator: + import: "package:yaroorm_builder/builder.dart" + builder_factories: ["yaroormBuilder"] + build_extensions: { ".dart": [".g.part"] } + auto_apply: dependents + build_to: cache + applies_builders: ["source_gen|combining_builder"] diff --git a/packages/generator/lib/builder.dart b/packages/generator/lib/builder.dart new file mode 100644 index 00000000..da4ffcbd --- /dev/null +++ b/packages/generator/lib/builder.dart @@ -0,0 +1,7 @@ +import 'package:build/build.dart'; + +import 'src/generator.dart'; + +/// Builds generators for `build_runner` to run +Builder yaroormBuilder(BuilderOptions options) => + generatorFactoryBuilder(options); diff --git a/packages/generator/lib/src/generator.dart b/packages/generator/lib/src/generator.dart new file mode 100644 index 00000000..a98d4515 --- /dev/null +++ b/packages/generator/lib/src/generator.dart @@ -0,0 +1,321 @@ +import 'package:analyzer/dart/constant/value.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/nullability_suffix.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:build/build.dart'; +import 'package:code_builder/code_builder.dart'; +import 'package:dart_style/dart_style.dart'; +import 'package:recase/recase.dart'; +import 'package:source_gen/source_gen.dart'; + +// ignore: implementation_imports +import 'package:yaroorm/src/database/entity/entity.dart' as entity; + +class YaroormOptions { + YaroormOptions.fromOptions([BuilderOptions? options]); +} + +Builder generatorFactoryBuilder(BuilderOptions options) => SharedPartBuilder( + [YaroormGenerator(YaroormOptions.fromOptions(options))], + 'yaroorm', + ); + +class YaroormGenerator extends GeneratorForAnnotation { + final YaroormOptions globalOptions; + + YaroormGenerator(this.globalOptions); + + @override + generateForAnnotatedElement( + Element element, + ConstantReader annotation, + BuildStep buildStep, + ) { + if (element is! ClassElement) { + final name = element.displayName; + throw InvalidGenerationSourceError( + 'Generator cannot target `$name`.', + todo: 'Remove the [Table] annotation from `$name`.', + ); + } + + return _implementClass(element, annotation); + } + + TypeChecker _typeChecker(Type type) => TypeChecker.fromRuntime(type); + + ({FieldElement field, ConstantReader reader})? _getFieldAnnotationByType( + List fields, Type type) { + for (final field in fields) { + final result = + _typeChecker(type).firstAnnotationOf(field, throwOnUnresolved: false); + if (result != null) { + return (field: field, reader: ConstantReader(result)); + } + } + return null; + } + + String _implementClass(ClassElement classElement, ConstantReader annotation) { + final fields = classElement.fields.where(allowedTypes).toList(); + final className = classElement.name; + + final tableName = annotation.peek('name')!.stringValue; + + final primaryKey = _getFieldAnnotationByType(fields, entity.PrimaryKey); + final createdAtField = + _getFieldAnnotationByType(fields, entity.CreatedAtColumn)?.field; + final updatedAtField = + _getFieldAnnotationByType(fields, entity.UpdatedAtColumn)?.field; + + final converters = annotation.peek('converters')!.listValue; + + if (primaryKey == null) { + throw Exception("$className Entity doesn't have primary key"); + } + + final autoIncrementPrimaryKey = + primaryKey.reader.peek('autoIncrement')!.boolValue; + final timestampsEnabled = (createdAtField ?? updatedAtField) != null; + + /// other properties aside primarykey, updatedAt and createdAt + final normalFields = fields.where( + (e) => ![createdAtField, updatedAtField, primaryKey.field].contains(e)); + + final creatableFields = [ + if (!autoIncrementPrimaryKey) primaryKey.field, + ...normalFields + ]; + + final primaryConstructor = + classElement.constructors.firstWhereOrNull((e) => e.name == ""); + if (primaryConstructor == null) { + throw '$className Entity does not have a default constructor'; + } + + final fieldNames = fields.map((e) => e.name); + final notAllowedProps = + primaryConstructor.children.where((e) => !fieldNames.contains(e.name)); + if (notAllowedProps.isNotEmpty) { + throw Exception( + 'These props are not allowed in $className Entity default constructor: ${notAllowedProps.join(', ')}'); + } + + String fieldToString(FieldElement e) { + final symbol = '#${e.name}'; + + final requiredOpts = ''' + "${e.name}", + ${e.type.getDisplayString(withNullability: false)}, + $symbol + '''; + + if (e == primaryKey.field) { + return '''PrimaryKeyField( + $requiredOpts) + ${!autoIncrementPrimaryKey ? ', autoIncrement: false' : ''}'''; + } + + return '''DBEntityField( + $requiredOpts + ${e.type.isNullable ? ', nullable: true' : ''})''' + .trim(); + } + + final typeDataName = '${className.snakeCase}TypeData'; + final queryName = '${className}Query'; + + final library = Library((b) => b + ..comments.add('ignore_for_file: non_constant_identifier_names') + ..body.addAll([ + Method((m) => m + ..name = queryName + ..returns = refer('Query<$className>') + ..type = MethodType.getter + ..lambda = true + ..body = Code('DB.query<$className>()')), + Method((m) => m + ..name = '${classElement.name.pascalCase}Schema' + ..returns = refer('CreateSchema') + ..lambda = true + ..type = MethodType.getter + ..body = Code('Schema.fromEntity<$className>()')), + Method((m) => m + ..name = typeDataName + ..returns = refer('DBEntity<$className>') + ..type = MethodType.getter + ..lambda = true + ..body = Code( + '''DBEntity<$className>( + "$tableName", + timestampsEnabled: $timestampsEnabled, + columns: ${fields.map(fieldToString).toList()}, + mirror: _\$${className}EntityMirror.new, + build: (args) => ${_generateConstructorCode(className, primaryConstructor)}, + ${converters.isEmpty ? '' : 'converters: ${converters.map(processAnnotation).toList()},'})''', + )), + Class( + (b) => b + ..name = '_\$${className}EntityMirror' + ..extend = refer('EntityMirror<$className>') + ..constructors.add(Constructor( + (b) => b + ..constant = true + ..requiredParameters.add(Parameter((p) => p + ..toSuper = true + ..name = 'instance')), + )) + ..methods.addAll([ + Method((m) => m + ..name = 'get' + ..annotations.add(CodeExpression(Code('override'))) + ..requiredParameters.add(Parameter((p) => p + ..name = 'field' + ..type = refer('Symbol'))) + ..returns = refer('Object?') + ..body = Code(''' +return switch(field) { + ${fields.map((e) => ''' + #${e.name} => instance.${e.name} +''').join(',')}, + _ => throw Exception('Unknown property \$field'), +}; +''')), + ]), + ), + Extension((b) => b + ..name = '${className}QueryExtension' + ..on = refer('Query<$className>') + ..methods.addAll([ + Method( + (m) => m + ..name = 'create' + ..returns = refer('Future<$className>') + ..lambda = true + ..optionalParameters.addAll(creatableFields.map( + (field) => Parameter((p) => p + ..name = field.name + ..named = true + ..type = refer('${field.type}') + ..required = !field.type.isNullable), + )) + ..body = Code('''insert({ + ${creatableFields.map((e) => '#${e.name}: ${e.name}').join(',')} + ,})'''), + ), + Method( + (m) => m + ..name = 'update' + ..returns = refer('Future') + ..modifier = MethodModifier.async + ..optionalParameters.addAll([ + Parameter((p) => p + ..name = 'where' + ..named = true + ..type = refer('WhereBuilder<$className>') + ..required = true), + Parameter((p) => p + ..name = 'value' + ..named = true + ..type = refer(className) + ..required = true) + ]) + ..body = Code(''' + final mirror = $typeDataName.mirror(value); + final props = { + for (final column in $typeDataName.columns) column.dartName: mirror.get(column.dartName), + }; + + final update = UpdateQuery( + entity.tableName, + whereClause: where(this), + data: conformToDbTypes(props, converters), + ); + + await accept(update); +'''), + ), + ])) + ])); + + final emitter = DartEmitter(useNullSafetySyntax: true); + return DartFormatter().format('${library.accept(emitter)}'); + } + + bool allowedTypes(FieldElement field) { + return field.getter?.isSynthetic ?? false; + } + + String _generateConstructorCode( + String className, ConstructorElement constructor) { + final sb = StringBuffer()..write('$className('); + + final normalParams = constructor.type.normalParameterNames; + final namedParams = constructor.type.namedParameterTypes.keys; + + if (normalParams.isNotEmpty) { + sb + ..write(normalParams.map((name) => 'args[#$name]').join(',')) + ..write(','); + } + + if (namedParams.isNotEmpty) { + sb + ..writeln(namedParams.map((name) => '$name: args[#$name]').join(', ')) + ..write(','); + } + + return (sb..write(')')).toString(); + } + + /// Returns the field and it's meta value + // ({FieldElement field, DartObject meta})? getEntityField( + // List fields, String type) { + // ElementAnnotation? metaData; + + // final field = fields.firstWhereOrNull((f) { + // metaData = f.metadata.firstWhereOrNull((e) => + // e.element?.library?.identifier == yaroormIdentifier && + // (e.element is PropertyAccessorElement) && + // (e.element as PropertyAccessorElement).returnType.toString() == type); + + // return metaData != null; + // }); + // if (field == null) return null; + + // return (field: field, meta: metaData!.computeConstantValue()!); + // } + + /// Process entity annotation + String processAnnotation(DartObject constantValue) { + final classElement = constantValue.type!.element as ClassElement; + assert(classElement.supertype!.typeArguments.length == 2, + 'Should have two type arguments'); + + final variable = constantValue.variable; + if (variable != null) return variable.name; + + final custructor = classElement.constructors.first; + if (custructor.parameters.isEmpty) { + return '${classElement.name}()'; + } + + /// TODO(codekeyz): resolve constructor for TypeConverters + throw UnsupportedError('Parameters for TypeConverters not yet supported'); + } +} + +extension DartTypeExt on DartType { + bool get isNullable => nullabilitySuffix == NullabilitySuffix.question; +} + +extension IterableExtension on Iterable { + T? firstWhereOrNull(bool Function(T element) test) { + for (final element in this) { + if (test(element)) { + return element; + } + } + return null; + } +} diff --git a/packages/generator/pubspec.yaml b/packages/generator/pubspec.yaml new file mode 100644 index 00000000..b7560a64 --- /dev/null +++ b/packages/generator/pubspec.yaml @@ -0,0 +1,19 @@ +name: yaroorm_builder +description: A sample command-line application. +version: 1.0.0 + +environment: + sdk: ^3.0.0 + +dependencies: + analyzer: '>=5.13.0 <7.0.0' + code_builder: ^4.10.0 + source_gen: ^1.5.0 + dart_style: + build: ^2.3.1 + yaroorm: + recase: ^4.1.0 + +dev_dependencies: + lints: ^3.0.0 + test: ^1.24.0 diff --git a/packages/yaroorm/lib/src/config.dart b/packages/yaroorm/lib/src/config.dart index 16d7386d..935966aa 100644 --- a/packages/yaroorm/lib/src/config.dart +++ b/packages/yaroorm/lib/src/config.dart @@ -1,5 +1,5 @@ import 'database/driver/driver.dart'; -import '../migration.dart'; +import 'migration.dart'; class YaroormConfig { final String defaultConnName; diff --git a/packages/yaroorm/lib/src/database/driver/driver.dart b/packages/yaroorm/lib/src/database/driver/driver.dart index 2b7186fe..ca2cf9b4 100644 --- a/packages/yaroorm/lib/src/database/driver/driver.dart +++ b/packages/yaroorm/lib/src/database/driver/driver.dart @@ -3,7 +3,7 @@ import 'package:yaroorm/src/database/driver/pgsql_driver.dart'; import '../../primitives/serializer.dart'; import '../../query/query.dart'; -import '../../../migration.dart'; +import '../../migration.dart'; import '../entity/entity.dart'; import 'sqlite_driver.dart'; diff --git a/packages/yaroorm/lib/src/database/driver/mysql_driver.dart b/packages/yaroorm/lib/src/database/driver/mysql_driver.dart index 8cc381d1..04d42e9a 100644 --- a/packages/yaroorm/lib/src/database/driver/mysql_driver.dart +++ b/packages/yaroorm/lib/src/database/driver/mysql_driver.dart @@ -1,6 +1,6 @@ import 'package:meta/meta.dart'; import 'package:mysql_client/mysql_client.dart'; -import 'package:yaroorm/migration.dart'; +import 'package:yaroorm/src/migration.dart'; import 'package:yaroorm/src/database/entity/entity.dart'; import 'package:yaroorm/src/query/query.dart'; diff --git a/packages/yaroorm/lib/src/database/driver/pgsql_driver.dart b/packages/yaroorm/lib/src/database/driver/pgsql_driver.dart index 6f9b1e6a..fbb3e51f 100644 --- a/packages/yaroorm/lib/src/database/driver/pgsql_driver.dart +++ b/packages/yaroorm/lib/src/database/driver/pgsql_driver.dart @@ -1,7 +1,7 @@ import 'package:meta/meta.dart'; import 'package:postgres/postgres.dart' as pg; -import '../../../migration.dart'; +import '../../migration.dart'; import '../../primitives/serializer.dart'; import '../../query/query.dart'; import '../entity/entity.dart'; diff --git a/packages/yaroorm/lib/src/database/driver/sqlite_driver.dart b/packages/yaroorm/lib/src/database/driver/sqlite_driver.dart index 091978e1..9fa478f3 100644 --- a/packages/yaroorm/lib/src/database/driver/sqlite_driver.dart +++ b/packages/yaroorm/lib/src/database/driver/sqlite_driver.dart @@ -2,7 +2,7 @@ import 'package:meta/meta.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:sqflite_common/sql.dart'; import 'package:collection/collection.dart'; -import 'package:yaroorm/migration.dart'; +import 'package:yaroorm/src/migration.dart'; import 'package:yaroorm/src/query/aggregates.dart'; import '../../primitives/serializer.dart'; diff --git a/packages/yaroorm/lib/src/database/entity/entity.dart b/packages/yaroorm/lib/src/database/entity/entity.dart index a9186d49..4b4b887c 100644 --- a/packages/yaroorm/lib/src/database/entity/entity.dart +++ b/packages/yaroorm/lib/src/database/entity/entity.dart @@ -1,3 +1,4 @@ +import 'package:copy_with_extension/copy_with_extension.dart'; import 'package:recase/recase.dart'; import 'package:meta/meta_meta.dart'; @@ -23,13 +24,13 @@ abstract class Entity { } @Target({TargetKind.classType}) -class Table { +class Table extends CopyWith { final String name; - final List? converters; + final List converters; const Table( this.name, { - this.converters, + this.converters = const [], }); } @@ -44,7 +45,7 @@ class TableColumn { @Target({TargetKind.field}) class PrimaryKey extends TableColumn { final bool autoIncrement; - const PrimaryKey({this.autoIncrement = true}); + const PrimaryKey({this.autoIncrement = true, super.name}); } @Target({TargetKind.field}) diff --git a/packages/yaroorm/lib/migration.dart b/packages/yaroorm/lib/src/migration.dart similarity index 99% rename from packages/yaroorm/lib/migration.dart rename to packages/yaroorm/lib/src/migration.dart index 366ca487..5aec975d 100644 --- a/packages/yaroorm/lib/migration.dart +++ b/packages/yaroorm/lib/src/migration.dart @@ -4,7 +4,7 @@ import 'package:meta/meta.dart'; import 'package:recase/recase.dart'; import 'package:yaroorm/yaroorm.dart'; -import 'src/reflection.dart'; +import 'reflection.dart'; abstract class TableBlueprint { void id({String name = 'id', String? type, bool autoIncrement = true}); diff --git a/packages/yaroorm/lib/src/primitives/serializer.dart b/packages/yaroorm/lib/src/primitives/serializer.dart index a292fe4c..e863cbc8 100644 --- a/packages/yaroorm/lib/src/primitives/serializer.dart +++ b/packages/yaroorm/lib/src/primitives/serializer.dart @@ -1,4 +1,4 @@ -import 'package:yaroorm/migration.dart'; +import 'package:yaroorm/src/migration.dart'; import 'package:yaroorm/src/query/aggregates.dart'; import '../query/query.dart'; diff --git a/packages/yaroorm/lib/src/utils.dart b/packages/yaroorm/lib/src/utils.dart new file mode 100644 index 00000000..193d9aa4 --- /dev/null +++ b/packages/yaroorm/lib/src/utils.dart @@ -0,0 +1,10 @@ +extension IterableExtension on Iterable { + T? firstWhereOrNull(bool Function(T element) test) { + for (final element in this) { + if (test(element)) { + return element; + } + } + return null; + } +} diff --git a/packages/yaroorm/lib/yaroorm.dart b/packages/yaroorm/lib/yaroorm.dart index 5aa63fda..a2581784 100644 --- a/packages/yaroorm/lib/yaroorm.dart +++ b/packages/yaroorm/lib/yaroorm.dart @@ -6,3 +6,6 @@ export 'src/database/driver/driver.dart'; export 'src/database/entity/entity.dart'; export 'src/database/database.dart'; export 'src/config.dart'; +export 'src/reflection.dart'; +export 'src/migration.dart'; +export 'package:copy_with_extension/copy_with_extension.dart'; diff --git a/packages/yaroorm/pubspec.yaml b/packages/yaroorm/pubspec.yaml index 7f554b7b..c402b1a6 100644 --- a/packages/yaroorm/pubspec.yaml +++ b/packages/yaroorm/pubspec.yaml @@ -21,16 +21,17 @@ dependencies: postgres: ^3.1.1 meta: ^1.11.0 - collection: ^1.18.0 grammer: ^1.0.3 recase: ^4.1.0 + copy_with_extension: ^5.0.4 dev_dependencies: + yaroorm_builder: lints: ^3.0.0 test: ^1.24.0 yaroo_cli: path: ../yaroo_cli path: ^1.9.0 - build_runner: - copy_with_extension: ^5.0.4 copy_with_extension_gen: ^5.0.4 + build_runner: + collection: ^1.18.0 diff --git a/packages/yaroorm/test/integration/e2e_basic.dart b/packages/yaroorm/test/integration/e2e_basic.dart index ea12e07b..f39c9a07 100644 --- a/packages/yaroorm/test/integration/e2e_basic.dart +++ b/packages/yaroorm/test/integration/e2e_basic.dart @@ -1,47 +1,60 @@ -import 'package:collection/collection.dart'; import 'package:test/test.dart'; import 'package:yaroorm/yaroorm.dart'; +import 'package:collection/collection.dart'; +import '../models/models.dart'; import 'fixtures/migrator.dart'; import 'fixtures/test_data.dart'; void runBasicE2ETest(String connectionName) { - final db = DB.connection(connectionName); + final _ = DB.connection(connectionName); + final userQuery = UserQuery.driver(_.driver); - return group('with ${db.driver.type.name} driver', () { + return group('with ${_.driver.type.name} driver', () { test('driver should connect', () async { - await db.driver.connect(); + await _.driver.connect(); - expect(db.driver.isOpen, isTrue); + expect(_.driver.isOpen, isTrue); }); test('should have no tables', - () async => expect(await db.driver.hasTable('users'), isFalse)); + () async => expect(await _.driver.hasTable('users'), isFalse)); test('should execute migration', () async { await runMigrator(connectionName, 'migrate'); - expect(await db.driver.hasTable('users'), isTrue); + expect(await _.driver.hasTable('users'), isTrue); }); test('should insert single user', () async { - final result = await usersList.first.withDriver(db.driver).save(); + final firstData = usersList.first; + final result = await userQuery.create( + firstname: firstData.firstname, + lastname: firstData.lastname, + age: firstData.age, + homeAddress: firstData.homeAddress, + ); + expect(result, isA().having((p0) => p0.id, 'has primary key', 1)); - expect(await db.query().all(), hasLength(1)); + expect(await userQuery.all(), hasLength(1)); }); test('should insert many users', () async { - final remainingUsers = - usersList.sublist(1).map((e) => e.to_db_data).toList(); - final userQuery = db.query(); - await userQuery.insertMany(remainingUsers); - - expect(await userQuery.all(), hasLength(usersList.length)); + await Future.wait(usersList + .sublist(1) + .map((e) => userQuery.create( + firstname: e.firstname, + lastname: e.lastname, + age: e.age, + homeAddress: e.homeAddress)) + .toList()); + + expect(await UserQuery.count(), hasLength(usersList.length)); }); group('Aggregate Functions', () { - final query = db.query().isLike('home_address', '%%, Ghana'); + final query = userQuery.isLike('home_address', '%%, Ghana'); List usersInGhana = []; setUpAll(() async { @@ -76,7 +89,7 @@ void runBasicE2ETest(String connectionName) { test('concat', () async { Matcher matcher(String separator) { if ([DatabaseDriverType.sqlite, DatabaseDriverType.pgsql] - .contains(db.driver.type)) { + .contains(_.driver.type)) { return equals(usersInGhana.map((e) => e.age).join(separator)); } @@ -89,116 +102,116 @@ void runBasicE2ETest(String connectionName) { }); }); - test('should update user', () async { - final user = await db.query().get(); - expect(user!.id, 1); - - final updatedUser = - await user.copyWith(firstname: 'Red Oil', age: 100).save(); - - final userFromDB = await db.query().get(user.id); - expect(user, isNotNull); - expect(userFromDB?.firstname, 'Red Oil'); - expect(userFromDB?.age, 100); - }); - - test('should update many users', () async { - final userQuery = db.query(); - - final age50Users = userQuery.equal('age', 50); - final usersWithAge50 = await age50Users.findMany(); - expect(usersWithAge50.length, 4); - expect(usersWithAge50.every((e) => e.age == 50), isTrue); - - await userQuery.update( - where: (query) => query.equal('age', 50), - values: {'home_address': 'Keta, Ghana'}).execute(); - - final updatedResult = await age50Users.findMany(); - expect(updatedResult.length, 4); - expect(updatedResult.every((e) => e.age == 50), isTrue); - expect( - updatedResult.every((e) => e.homeAddress == 'Keta, Ghana'), isTrue); - }); - - test('should fetch only users in Ghana', () async { - final query = db - .query() - .isLike('home_address', '%, Ghana') - .orderByDesc('age'); - final usersInGhana = await query.findMany(); - expect(usersInGhana.length, 10); - expect( - usersInGhana.every((e) => e.homeAddress.contains('Ghana')), - isTrue, - ); - - expect(await query.take(4), hasLength(4)); - }); - - test('should get all users between age 35 and 50', () async { - final age50Users = await db - .query() - .isBetween('age', [35, 50]) - .orderByDesc('age') - .findMany(); - expect(age50Users.length, 19); - expect(age50Users.first.age, 50); - expect(age50Users.last.age, 35); - }); - - test('should get all users in somewhere in Nigeria', () async { - final users = await db - .query() - .isLike('home_address', '%, Nigeria') - .orderByAsc('home_address') - .findMany(); - - expect(users.length, 18); - expect(users.first.homeAddress, 'Abuja, Nigeria'); - expect(users.last.homeAddress, 'Owerri, Nigeria'); - }); - - test('should get all users where age is 30 or 52', () async { - final users = await db - .query() - .equal('age', 30) - .orWhere('age', '=', 52) - .findMany(); - expect(users.every((e) => [30, 52].contains(e.age)), isTrue); - }); - - test('should delete user', () async { - final userOne = await db.query().get(); - expect(userOne, isNotNull); - - await userOne!.delete(); - - final usersAfterDelete = await db.query().all(); - expect(usersAfterDelete.any((e) => e.id == userOne.id), isFalse); - }); - - test('should delete many users', () async { - final query = db.query().isLike('home_address', '%, Nigeria'); - expect(await query.findMany(), isNotEmpty); - - await query.delete(); - - expect(await query.findMany(), isEmpty); - }); + // test('should update user', () async { + // final user = await userQuery.get(); + // expect(user!.id, 1); + + // await userQuery.update( + // where: (query) => query.equal('id', user.id), + // value: user.copyWith(firstname: 'Red Oil', age: 100), + // ); + + // final userFromDB = await userQuery.get(user.id); + // expect(user, isNotNull); + // expect(userFromDB?.firstname, 'Red Oil'); + // expect(userFromDB?.age, 100); + // }); + + // test('should update many users', () async { + // final age50Users = userQuery.equal('age', 50); + // final usersWithAge50 = await age50Users.findMany(); + // expect(usersWithAge50.length, 4); + // expect(usersWithAge50.every((e) => e.age == 50), isTrue); + + // await userQuery.update( + // where: (query) => query.equal('age', 50), + // value: {'home_address': 'Keta, Ghana'}).execute(); + + // final updatedResult = await age50Users.findMany(); + // expect(updatedResult.length, 4); + // expect(updatedResult.every((e) => e.age == 50), isTrue); + // expect( + // updatedResult.every((e) => e.homeAddress == 'Keta, Ghana'), isTrue); + // }); + + // test('should fetch only users in Ghana', () async { + // final query = db + // .query() + // .isLike('home_address', '%, Ghana') + // .orderByDesc('age'); + // final usersInGhana = await query.findMany(); + // expect(usersInGhana.length, 10); + // expect( + // usersInGhana.every((e) => e.homeAddress.contains('Ghana')), + // isTrue, + // ); + + // expect(await query.take(4), hasLength(4)); + // }); + + // test('should get all users between age 35 and 50', () async { + // final age50Users = await db + // .query() + // .isBetween('age', [35, 50]) + // .orderByDesc('age') + // .findMany(); + // expect(age50Users.length, 19); + // expect(age50Users.first.age, 50); + // expect(age50Users.last.age, 35); + // }); + + // test('should get all users in somewhere in Nigeria', () async { + // final users = await db + // .query() + // .isLike('home_address', '%, Nigeria') + // .orderByAsc('home_address') + // .findMany(); + + // expect(users.length, 18); + // expect(users.first.homeAddress, 'Abuja, Nigeria'); + // expect(users.last.homeAddress, 'Owerri, Nigeria'); + // }); + + // test('should get all users where age is 30 or 52', () async { + // final users = await db + // .query() + // .equal('age', 30) + // .orWhere('age', '=', 52) + // .findMany(); + // expect(users.every((e) => [30, 52].contains(e.age)), isTrue); + // }); + + // test('should delete user', () async { + // final userOne = await db.query().get(); + // expect(userOne, isNotNull); + + // await userOne!.delete(); + + // final usersAfterDelete = await db.query().all(); + // expect(usersAfterDelete.any((e) => e.id == userOne.id), isFalse); + // }); + + // test('should delete many users', () async { + // final query = db.query().isLike('home_address', '%, Nigeria'); + // expect(await query.findMany(), isNotEmpty); + + // await query.delete(); + + // expect(await query.findMany(), isEmpty); + // }); test('should drop tables', () async { await runMigrator(connectionName, 'migrate:reset'); - expect(await db.driver.hasTable('users'), isFalse); + expect(await _.driver.hasTable('users'), isFalse); }); test('should disconnect', () async { - expect(db.driver.isOpen, isTrue); + expect(_.driver.isOpen, isTrue); - await db.driver.disconnect(); + await _.driver.disconnect(); - expect(db.driver.isOpen, isFalse); + expect(_.driver.isOpen, isFalse); }); }); } diff --git a/packages/yaroorm/test/integration/fixtures/migrations.dart b/packages/yaroorm/test/integration/fixtures/migrations.dart index 88506959..85c39efd 100644 --- a/packages/yaroorm/test/integration/fixtures/migrations.dart +++ b/packages/yaroorm/test/integration/fixtures/migrations.dart @@ -1,53 +1,35 @@ -import 'package:yaroorm/migration.dart'; -import 'package:yaroorm/src/database/driver/sqlite_driver.dart'; +import 'package:yaroorm/src/migration.dart'; -import 'test_data.dart'; -import 'migrations.reflectable.dart'; +import '../../models/models.dart'; class AddUsersTable extends Migration { @override void up(List schemas) { - schemas.add(Schema.fromEntity(User)); + schemas.add(UserSchema); } @override void down(List schemas) { - schemas.add(Schema.dropIfExists(User)); + schemas.add(Schema.dropIfExists(UserSchema)); } } class AddPostsTable extends Migration { @override void up(List schemas) { - final postSchema = Schema.create('posts', (table) { - return table - ..id() - ..integer('userId') - ..string('title') - ..string('description') - ..foreign( - onKey: (key) => key.actions( - onUpdate: ForeignKeyAction.cascade, - onDelete: ForeignKeyAction.cascade)) - ..timestamps(); - }); + final postSchema = PostSchema + ..foreign( + onKey: (key) => key.actions( + onUpdate: ForeignKeyAction.cascade, + onDelete: ForeignKeyAction.cascade)); - final postCommentSchema = Schema.create('post_comments', (table) { - return table - ..id(type: 'VARCHAR(255)', autoIncrement: false) - ..integer('postId') - ..string('comment') - ..foreign() - ..timestamps(); - }); - - schemas.addAll([postSchema, postCommentSchema]); + schemas.addAll([postSchema, PostCommentSchema]); } @override void down(List schemas) { - schemas.add(Schema.dropIfExists('post_comments')); + schemas.add(Schema.dropIfExists(PostCommentSchema)); - schemas.add(Schema.dropIfExists(Post)); + schemas.add(Schema.dropIfExists(PostSchema)); } } diff --git a/packages/yaroorm/test/integration/fixtures/migrator.dart b/packages/yaroorm/test/integration/fixtures/migrator.dart index 391d174d..aef9a39a 100644 --- a/packages/yaroorm/test/integration/fixtures/migrator.dart +++ b/packages/yaroorm/test/integration/fixtures/migrator.dart @@ -1,12 +1,10 @@ import 'dart:io'; import 'package:test/test.dart'; -import 'package:yaroo_cli/orm/orm.dart'; +import 'package:yaroo_cli/orm.dart'; import '../fixtures/orm_config.dart' as conf; -import 'migrator.reflectable.dart'; void main(List args) async { - initializeReflectable(); await OrmCLIRunner.start(args, conf.config); } diff --git a/packages/yaroorm/test/models/models.dart b/packages/yaroorm/test/models/models.dart index e8f22d7e..7a789944 100644 --- a/packages/yaroorm/test/models/models.dart +++ b/packages/yaroorm/test/models/models.dart @@ -1,10 +1,8 @@ -import 'package:copy_with_extension/copy_with_extension.dart'; import 'package:yaroorm/yaroorm.dart'; -import 'package:yaroorm/src/reflection.dart'; part 'models.g.dart'; -@CopyWith() +@Table('users') class User extends Entity { @primaryKey final int id; @@ -27,40 +25,40 @@ class User extends Entity { // HasMany get posts => hasMany(); } -// @CopyWith() -// @Table('posts') -// class Post extends Entity { -// @primaryKey -// final int id; - -// final String title; -// final String description; +@Table('posts') +class Post extends Entity { + @primaryKey + final int id; -// final int userId; + final String title; + final String description; -// @createdAtCol -// final DateTime createdAt; + final int userId; -// @updatedAtCol -// final DateTime updatedAt; + @createdAtCol + final DateTime createdAt; -// Post( -// this.id, -// this.title, -// this.description, { -// required this.userId, -// required this.createdAt, -// required this.updatedAt, -// }); + @updatedAtCol + final DateTime updatedAt; -// // HasMany get comments => hasMany(); -// } + Post( + this.id, + this.title, + this.description, { + required this.userId, + required this.createdAt, + required this.updatedAt, + }); + // HasMany get comments => hasMany(); +} -// @Table('post_comments', enableTimestamps: true) -// class PostComment extends Entity { -// final String comment; -// final int? postId; +@Table('post_comments') +class PostComment extends Entity { + @primaryKey + final String id; + final String comment; + final int? postId; -// PostComment(this.comment, {this.postId}); -// } + PostComment(this.id, this.comment, {this.postId}); +} diff --git a/packages/yaroorm/test/unit/dialects/sqlite_test.dart b/packages/yaroorm/test/unit/dialects/sqlite_test.dart index 385650f9..fb51cb48 100644 --- a/packages/yaroorm/test/unit/dialects/sqlite_test.dart +++ b/packages/yaroorm/test/unit/dialects/sqlite_test.dart @@ -1,30 +1,33 @@ import 'package:test/test.dart'; -import 'package:yaroorm/migration.dart'; -import 'package:yaroorm/src/database/driver/sqlite_driver.dart'; import 'package:yaroorm/yaroorm.dart'; import '../../integration/fixtures/orm_config.dart' as db; -import '../../integration/fixtures/test_data.dart'; -import 'sqlite_test.reflectable.dart'; -@Table(name: 'user_articles', primaryKey: '_id_') -class Article extends Entity { +part 'sqlite_test.g.dart'; + +@Table('user_articles') +class Article extends Entity { + @PrimaryKey(name: '_id_') + final int id; + final String name; final int ownerId; - Article(this.name, this.ownerId); + Article(this.id, this.name, this.ownerId); } -class ArticleComment extends Entity { +@Table('article_comments') +class ArticleComment extends Entity { + @primaryKey + final int id; + final int articleId; final int userId; - ArticleComment(this.articleId, this.userId); + ArticleComment(this.id, this.articleId, this.userId); } void main() { - initializeReflectable(); - DB.init(db.config); late DatabaseDriver driver; @@ -54,23 +57,23 @@ void main() { 'SELECT * FROM users ORDER BY names DESC, ages ASC;'); }); - test('when update', () { - final query = Query.table('users').driver(driver).update( - where: (where) => where.where('name', '=', 'Chima'), - values: {'firstname': 'Chima', 'lastname': 'Precious'}, - ); + // test('when update', () { + // final query = Query.table('users').driver(driver).update( + // where: (where) => where.where('name', '=', 'Chima'), + // values: {'firstname': 'Chima', 'lastname': 'Precious'}, + // ); - expect(query.statement, - 'UPDATE users SET firstname = ?, lastname = ? WHERE name = \'Chima\';'); - }); + // expect(query.statement, + // 'UPDATE users SET firstname = ?, lastname = ? WHERE name = \'Chima\';'); + // }); - test('when delete', () { - final query = Query.table('users') - .driver(driver) - .delete((where) => where.where('name', '=', 'Chima')); + // test('when delete', () { + // final query = Query.table('users') + // .driver(driver) + // .delete((where) => where.where('name', '=', 'Chima')); - expect(query.statement, 'DELETE FROM users WHERE name = \'Chima\';'); - }); + // expect(query.statement, 'DELETE FROM users WHERE name = \'Chima\';'); + // }); group('when .where', () { test('of level 1', () { @@ -1014,147 +1017,147 @@ void main() { }); }); - group('SQLITE Table Blueprint', () { - // - group('`foreignKey` should resolve for ', () { - // - test('class with entity meta', () { - final blueprint = SqliteTableBlueprint() - ..string('name') - ..integer('userId'); - - late ForeignKey key; - blueprint.foreign(onKey: (fkey) => key = fkey); - - expect(key.table, 'user_articles'); - expect(key.column, 'userId'); - expect(key.foreignTable, 'users'); - expect(key.foreignTableColumn, 'id'); - }); - - test('class with no meta', () { - final blueprint = SqliteTableBlueprint()..string('userId'); - - late ForeignKey key; - blueprint.foreign(onKey: (fkey) => key = fkey); - - expect(key.table, 'article_comments'); - expect(key.column, 'userId'); - expect(key.foreignTable, 'users'); - expect(key.foreignTableColumn, 'id'); - }); - - test('custom foreign reference column', () { - final blueprint = SqliteTableBlueprint()..string('articleId'); - - late ForeignKey key; - blueprint.foreign( - column: 'articleId', onKey: (fkey) => key = fkey); - - expect(key.table, 'article_comments'); - expect(key.column, 'articleId'); - expect(key.foreignTable, 'user_articles'); - expect(key.foreignTableColumn, '_id_'); - }); - - test('should make statement', () { - final blueprint = SqliteTableBlueprint() - ..string('name') - ..integer('userId'); - - late ForeignKey key; - blueprint.foreign(onKey: (fkey) => key = fkey); - - final statement = SqliteSerializer().acceptForeignKey(blueprint, key); - expect(statement, 'FOREIGN KEY (userId) REFERENCES users(id)'); - }); - - test('when custom reference actions', () { - final blueprint = SqliteTableBlueprint() - ..string('name') - ..integer('userId'); - - late ForeignKey key; - blueprint.foreign( - onKey: (fkey) => key = fkey.actions( - onUpdate: ForeignKeyAction.cascade, - onDelete: ForeignKeyAction.setNull), - ); - - final statement = SqliteSerializer().acceptForeignKey(blueprint, key); - expect(statement, - 'FOREIGN KEY (userId) REFERENCES users(id) ON UPDATE CASCADE ON DELETE SET NULL'); - }); - - group('when constrained', () { - test('with no specified name', () { - final blueprint = SqliteTableBlueprint() - ..string('name') - ..integer('userId'); - - late ForeignKey key; - blueprint.foreign( - onKey: (fkey) => key = fkey.constrained()); - - final statement = SqliteSerializer().acceptForeignKey(blueprint, key); - expect( - statement, - 'CONSTRAINT fk_user_articles_userId_to_users_id FOREIGN KEY (userId) REFERENCES users(id)', - ); - }); - - test('with specified name', () { - final blueprint = SqliteTableBlueprint() - ..string('name') - ..integer('ownerId'); - - late ForeignKey key; - blueprint.foreign( - column: 'ownerId', - onKey: (fkey) => key = fkey - .actions( - onUpdate: ForeignKeyAction.cascade, - onDelete: ForeignKeyAction.setNull) - .constrained(name: 'fk_articles_users')); - - final statement = SqliteSerializer().acceptForeignKey(blueprint, key); - expect(statement, - 'CONSTRAINT fk_articles_users FOREIGN KEY (ownerId) REFERENCES users(id) ON UPDATE CASCADE ON DELETE SET NULL'); - }); - - test('should serialize foreign key in schema', () { - var schema = Schema.create('articles', (table) { - return table - ..id() - ..string('userId') - ..foreign( - onKey: (key) => key.constrained(name: 'some_constraint')); - }); - - expect( - schema.toScript(driver.blueprint), - 'CREATE TABLE articles (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, userId VARCHAR NOT NULL, CONSTRAINT some_constraint FOREIGN KEY (userId) REFERENCES users(id));', - ); - - schema = Schema.create('articles', (table) { - return table - ..id(autoIncrement: false) - ..string('ownerId') - ..foreign( - column: 'ownerId', - onKey: (key) => key - .constrained(name: 'some_constraint') - .actions( - onUpdate: ForeignKeyAction.cascade, - onDelete: ForeignKeyAction.cascade)); - }); - - expect( - schema.toScript(driver.blueprint), - 'CREATE TABLE articles (id INTEGER NOT NULL PRIMARY KEY, ownerId VARCHAR NOT NULL, CONSTRAINT some_constraint FOREIGN KEY (ownerId) REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE);', - ); - }); - }); - }); - }); + // group('SQLITE Table Blueprint', () { + // // + // group('`foreignKey` should resolve for ', () { + // // + // test('class with entity meta', () { + // final blueprint = SqliteTableBlueprint() + // ..string('name') + // ..integer('userId'); + + // late ForeignKey key; + // blueprint.foreign(onKey: (fkey) => key = fkey); + + // expect(key.table, 'user_articles'); + // expect(key.column, 'userId'); + // expect(key.foreignTable, 'users'); + // expect(key.foreignTableColumn, 'id'); + // }); + + // test('class with no meta', () { + // final blueprint = SqliteTableBlueprint()..string('userId'); + + // late ForeignKey key; + // blueprint.foreign(onKey: (fkey) => key = fkey); + + // expect(key.table, 'article_comments'); + // expect(key.column, 'userId'); + // expect(key.foreignTable, 'users'); + // expect(key.foreignTableColumn, 'id'); + // }); + + // test('custom foreign reference column', () { + // final blueprint = SqliteTableBlueprint()..string('articleId'); + + // late ForeignKey key; + // blueprint.foreign( + // column: 'articleId', onKey: (fkey) => key = fkey); + + // expect(key.table, 'article_comments'); + // expect(key.column, 'articleId'); + // expect(key.foreignTable, 'user_articles'); + // expect(key.foreignTableColumn, '_id_'); + // }); + + // test('should make statement', () { + // final blueprint = SqliteTableBlueprint() + // ..string('name') + // ..integer('userId'); + + // late ForeignKey key; + // blueprint.foreign(onKey: (fkey) => key = fkey); + + // final statement = SqliteSerializer().acceptForeignKey(blueprint, key); + // expect(statement, 'FOREIGN KEY (userId) REFERENCES users(id)'); + // }); + + // test('when custom reference actions', () { + // final blueprint = SqliteTableBlueprint() + // ..string('name') + // ..integer('userId'); + + // late ForeignKey key; + // blueprint.foreign( + // onKey: (fkey) => key = fkey.actions( + // onUpdate: ForeignKeyAction.cascade, + // onDelete: ForeignKeyAction.setNull), + // ); + + // final statement = SqliteSerializer().acceptForeignKey(blueprint, key); + // expect(statement, + // 'FOREIGN KEY (userId) REFERENCES users(id) ON UPDATE CASCADE ON DELETE SET NULL'); + // }); + + // group('when constrained', () { + // test('with no specified name', () { + // final blueprint = SqliteTableBlueprint() + // ..string('name') + // ..integer('userId'); + + // late ForeignKey key; + // blueprint.foreign( + // onKey: (fkey) => key = fkey.constrained()); + + // final statement = SqliteSerializer().acceptForeignKey(blueprint, key); + // expect( + // statement, + // 'CONSTRAINT fk_user_articles_userId_to_users_id FOREIGN KEY (userId) REFERENCES users(id)', + // ); + // }); + + // test('with specified name', () { + // final blueprint = SqliteTableBlueprint() + // ..string('name') + // ..integer('ownerId'); + + // late ForeignKey key; + // blueprint.foreign( + // column: 'ownerId', + // onKey: (fkey) => key = fkey + // .actions( + // onUpdate: ForeignKeyAction.cascade, + // onDelete: ForeignKeyAction.setNull) + // .constrained(name: 'fk_articles_users')); + + // final statement = SqliteSerializer().acceptForeignKey(blueprint, key); + // expect(statement, + // 'CONSTRAINT fk_articles_users FOREIGN KEY (ownerId) REFERENCES users(id) ON UPDATE CASCADE ON DELETE SET NULL'); + // }); + + // test('should serialize foreign key in schema', () { + // var schema = Schema.create('articles', (table) { + // return table + // ..id() + // ..string('userId') + // ..foreign( + // onKey: (key) => key.constrained(name: 'some_constraint')); + // }); + + // expect( + // schema.toScript(driver.blueprint), + // 'CREATE TABLE articles (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, userId VARCHAR NOT NULL, CONSTRAINT some_constraint FOREIGN KEY (userId) REFERENCES users(id));', + // ); + + // schema = Schema.create('articles', (table) { + // return table + // ..id(autoIncrement: false) + // ..string('ownerId') + // ..foreign( + // column: 'ownerId', + // onKey: (key) => key + // .constrained(name: 'some_constraint') + // .actions( + // onUpdate: ForeignKeyAction.cascade, + // onDelete: ForeignKeyAction.cascade)); + // }); + + // expect( + // schema.toScript(driver.blueprint), + // 'CREATE TABLE articles (id INTEGER NOT NULL PRIMARY KEY, ownerId VARCHAR NOT NULL, CONSTRAINT some_constraint FOREIGN KEY (ownerId) REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE);', + // ); + // }); + // }); + // }); + // }); } From 4f8719a0ba198fa4aa594d7de0ba943a81a35eb4 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 7 Apr 2024 14:24:27 +0000 Subject: [PATCH 16/50] improvements --- .../lib/src/database/entity/converter.dart | 2 +- packages/yaroorm/lib/src/migration.dart | 25 ++++++---------- packages/yaroorm/lib/src/reflection.dart | 29 +++++++++++++++++-- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/packages/yaroorm/lib/src/database/entity/converter.dart b/packages/yaroorm/lib/src/database/entity/converter.dart index 371d1fb7..45aea068 100644 --- a/packages/yaroorm/lib/src/database/entity/converter.dart +++ b/packages/yaroorm/lib/src/database/entity/converter.dart @@ -57,7 +57,7 @@ Map conformToDbTypes( ) { final entity = Query.getEntity(); - Object toDbType(DBEntityField field) { + Object? toDbType(DBEntityField field) { final value = data[field.dartName]; final typeConverter = converters[field.type]; return typeConverter == null ? value : typeConverter.toDbType(value); diff --git a/packages/yaroorm/lib/src/migration.dart b/packages/yaroorm/lib/src/migration.dart index 5aec975d..30da3928 100644 --- a/packages/yaroorm/lib/src/migration.dart +++ b/packages/yaroorm/lib/src/migration.dart @@ -35,11 +35,9 @@ abstract class TableBlueprint { void integer(String name, {bool nullable = false, num? defaultValue}); - void double(String name, - {bool nullable = false, num? defaultValue, int? precision, int? scale}); + void double(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}); - void float(String name, - {bool nullable = false, num? defaultValue, int? precision, int? scale}); + void float(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}); void tinyInt(String name, {bool nullable = false, num? defaultValue}); @@ -49,11 +47,9 @@ abstract class TableBlueprint { void bigInteger(String name, {bool nullable = false, num? defaultValue}); - void decimal(String name, - {bool nullable = false, num? defaultValue, int? precision, int? scale}); + void decimal(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}); - void numeric(String name, - {bool nullable = false, num? defaultValue, int? precision, int? scale}); + void numeric(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}); void bit(String name, {bool nullable = false, int? defaultValue}); @@ -209,8 +205,7 @@ abstract class Schema { ); } - static Schema create(String name, TableBluePrintFunc func) => - CreateSchema._(name, func); + static Schema create(String name, TableBluePrintFunc func) => CreateSchema._(name, func); static Schema dropIfExists(CreateSchema value) { return _DropSchema(value.tableName); @@ -241,6 +236,7 @@ final class CreateSchema extends Schema { @override String toScript(TableBlueprint table) { table = _bluePrintFunc!.call(table); + for (final key in _foreignKeys) { table.foreign(key); } @@ -251,8 +247,7 @@ final class CreateSchema extends Schema { String? column, ForeignKey Function(ForeignKey fkey)? onKey, }) { - final referenceColumn = - column ?? '${ReferenceModel.toString().camelCase}Id'; + final referenceColumn = column ?? '${ReferenceModel.toString().camelCase}Id'; final referenceTable = getEntityTableName(); final referenceTablePrimaryKey = getEntityPrimaryKey(); @@ -280,8 +275,7 @@ class _RenameSchema extends Schema { _RenameSchema(String from, this.newName) : super._(from, null); @override - String toScript(TableBlueprint table) => - table.renameScript(tableName, newName); + String toScript(TableBlueprint table) => table.renameScript(tableName, newName); } enum ForeignKeyAction { cascade, restrict, setNull, setDefault, noAction } @@ -334,7 +328,6 @@ class ForeignKey { nullable: nullable, onUpdate: onUpdate, onDelete: onDelete, - constraint: name ?? - 'fk_${table}_${column}_to_${foreignTable}_$foreignTableColumn', + constraint: name ?? 'fk_${table}_${column}_to_${foreignTable}_$foreignTableColumn', ); } diff --git a/packages/yaroorm/lib/src/reflection.dart b/packages/yaroorm/lib/src/reflection.dart index 109746f6..265beaf6 100644 --- a/packages/yaroorm/lib/src/reflection.dart +++ b/packages/yaroorm/lib/src/reflection.dart @@ -1,4 +1,8 @@ -import 'package:yaroorm/yaroorm.dart'; + +import 'package:yaroorm/src/utils.dart'; + +import 'database/entity/entity.dart'; +import 'query/query.dart'; String getEntityTableName() => Query.getEntity().tableName; @@ -29,7 +33,15 @@ final class DBEntity { final bool timestampsEnabled; - PrimaryKeyField get primaryKey => columns.whereType().first; + PrimaryKeyField get primaryKey => columns.firstWhereOrNull((e) => e is PrimaryKeyField) as PrimaryKeyField; + + CreatedAtField? get createdAtField => !timestampsEnabled + ? null + : columns.firstWhereOrNull((e) => e is CreatedAtField) as CreatedAtField?; + + UpdatedAtField? get updatedAtField => !timestampsEnabled + ? null + : columns.firstWhereOrNull((e) => e is UpdatedAtField) as UpdatedAtField?; List get editableColumns => columns.where((e) => e != primaryKey).toList(); @@ -44,6 +56,9 @@ final class DBEntity { }); } + + + final class DBEntityField { /// dart name for property on Entity class final Symbol dartName; @@ -75,3 +90,13 @@ final class PrimaryKeyField extends DBEntityField { @override bool get primaryKey => true; } + +final class CreatedAtField extends DBEntityField { + const CreatedAtField(String columnName, Symbol dartName) + : super(columnName, DateTime, dartName); +} + +final class UpdatedAtField extends DBEntityField { + const UpdatedAtField(String columnName, Symbol dartName) + : super(columnName, DateTime, dartName); +} From df76b8b4778a39ab13519e45ac6e5e2aaf539e5a Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 7 Apr 2024 17:16:18 +0000 Subject: [PATCH 17/50] handle foreign key auto generation in migration schema --- packages/generator/lib/src/generator.dart | 78 ++++++++++----- .../lib/src/database/entity/entity.dart | 12 +++ packages/yaroorm/lib/src/migration.dart | 91 ++++++++--------- packages/yaroorm/lib/src/query/query.dart | 11 +-- .../yaroorm/lib/src/query/query_impl.dart | 16 +++ packages/yaroorm/lib/src/reflection.dart | 99 ++++++++++++++++--- 6 files changed, 212 insertions(+), 95 deletions(-) diff --git a/packages/generator/lib/src/generator.dart b/packages/generator/lib/src/generator.dart index a98d4515..9d6bc6a4 100644 --- a/packages/generator/lib/src/generator.dart +++ b/packages/generator/lib/src/generator.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:analyzer/dart/constant/value.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/nullability_suffix.dart'; @@ -45,7 +47,9 @@ class YaroormGenerator extends GeneratorForAnnotation { TypeChecker _typeChecker(Type type) => TypeChecker.fromRuntime(type); ({FieldElement field, ConstantReader reader})? _getFieldAnnotationByType( - List fields, Type type) { + List fields, + Type type, + ) { for (final field in fields) { final result = _typeChecker(type).firstAnnotationOf(field, throwOnUnresolved: false); @@ -101,17 +105,63 @@ class YaroormGenerator extends GeneratorForAnnotation { 'These props are not allowed in $className Entity default constructor: ${notAllowedProps.join(', ')}'); } - String fieldToString(FieldElement e) { + String generateCodeForField(FieldElement e) { final symbol = '#${e.name}'; + var columnName = e.name; + + final meta = _typeChecker(entity.TableColumn) + .firstAnnotationOf(e, throwOnUnresolved: false); + ConstantReader? metaReader; + + if (meta != null) { + metaReader = ConstantReader(meta); + final customName = metaReader.peek('name')?.stringValue; + if (customName != null) columnName = customName; + } final requiredOpts = ''' - "${e.name}", + "$columnName", ${e.type.getDisplayString(withNullability: false)}, $symbol '''; + if (meta != null) { + final isReferenceField = + _typeChecker(entity.reference).isExactly(meta.type!.element!); + + if (isReferenceField) { + final referencedType = metaReader!.peek('type')!.typeValue; + final element = referencedType.element as ClassElement; + final superType = element.supertype?.element; + + if (superType == null || !_typeChecker(entity.Entity).isExactly(superType)) { + throw InvalidGenerationSourceError( + 'Generator cannot target field `${e.name}` on `$className` class.', + todo: 'Type passed to [reference] annotation must be a subtype of `Entity`.', + ); + } + + final onUpdate = metaReader.peek('onUpdate')?.objectValue.variable!.name; + final onDelete = metaReader.peek('onDelete')?.objectValue.variable!.name; + + return '''DBEntityField.referenced<${element.name}>( + "$columnName", $symbol + ${onUpdate == null ? '' : ', onUpdate: ForeignKeyAction.$onUpdate'} + ${onDelete == null ? '' : ', onDelete: ForeignKeyAction.$onDelete'} + ,)'''; + } + } + + if (e == createdAtField) { + return '''DBEntityField.createdAt("$columnName", $symbol)'''; + } + + if (e == updatedAtField) { + return '''DBEntityField.updatedAt("$columnName", $symbol)'''; + } + if (e == primaryKey.field) { - return '''PrimaryKeyField( + return '''DBEntityField.primaryKey( $requiredOpts) ${!autoIncrementPrimaryKey ? ', autoIncrement: false' : ''}'''; } @@ -149,7 +199,7 @@ class YaroormGenerator extends GeneratorForAnnotation { '''DBEntity<$className>( "$tableName", timestampsEnabled: $timestampsEnabled, - columns: ${fields.map(fieldToString).toList()}, + columns: ${fields.map(generateCodeForField).toList()}, mirror: _\$${className}EntityMirror.new, build: (args) => ${_generateConstructorCode(className, primaryConstructor)}, ${converters.isEmpty ? '' : 'converters: ${converters.map(processAnnotation).toList()},'})''', @@ -268,24 +318,6 @@ return switch(field) { return (sb..write(')')).toString(); } - /// Returns the field and it's meta value - // ({FieldElement field, DartObject meta})? getEntityField( - // List fields, String type) { - // ElementAnnotation? metaData; - - // final field = fields.firstWhereOrNull((f) { - // metaData = f.metadata.firstWhereOrNull((e) => - // e.element?.library?.identifier == yaroormIdentifier && - // (e.element is PropertyAccessorElement) && - // (e.element as PropertyAccessorElement).returnType.toString() == type); - - // return metaData != null; - // }); - // if (field == null) return null; - - // return (field: field, meta: metaData!.computeConstantValue()!); - // } - /// Process entity annotation String processAnnotation(DartObject constantValue) { final classElement = constantValue.type!.element as ClassElement; diff --git a/packages/yaroorm/lib/src/database/entity/entity.dart b/packages/yaroorm/lib/src/database/entity/entity.dart index 4b4b887c..57065756 100644 --- a/packages/yaroorm/lib/src/database/entity/entity.dart +++ b/packages/yaroorm/lib/src/database/entity/entity.dart @@ -3,6 +3,7 @@ import 'package:recase/recase.dart'; import 'package:meta/meta_meta.dart'; +import '../../migration.dart'; import '../../query/query.dart'; import '../../reflection.dart'; @@ -58,6 +59,17 @@ class UpdatedAtColumn extends TableColumn { const UpdatedAtColumn() : super(name: 'updatedAt', nullable: false); } +/// Use this to reference other entities +/// +/// ignore: camel_case_types +class reference extends TableColumn { + final Type type; + + final ForeignKeyAction? onUpdate, onDelete; + + const reference(this.type, {super.name, this.onUpdate, this.onDelete}); +} + const primaryKey = PrimaryKey(); const createdAtCol = CreatedAtColumn(); const updatedAtCol = UpdatedAtColumn(); diff --git a/packages/yaroorm/lib/src/migration.dart b/packages/yaroorm/lib/src/migration.dart index 30da3928..500d2202 100644 --- a/packages/yaroorm/lib/src/migration.dart +++ b/packages/yaroorm/lib/src/migration.dart @@ -2,8 +2,9 @@ library migration; import 'package:meta/meta.dart'; import 'package:recase/recase.dart'; -import 'package:yaroorm/yaroorm.dart'; +import 'database/entity/entity.dart'; +import 'query/query.dart'; import 'reflection.dart'; abstract class TableBlueprint { @@ -35,9 +36,11 @@ abstract class TableBlueprint { void integer(String name, {bool nullable = false, num? defaultValue}); - void double(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}); + void double(String name, + {bool nullable = false, num? defaultValue, int? precision, int? scale}); - void float(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}); + void float(String name, + {bool nullable = false, num? defaultValue, int? precision, int? scale}); void tinyInt(String name, {bool nullable = false, num? defaultValue}); @@ -47,9 +50,11 @@ abstract class TableBlueprint { void bigInteger(String name, {bool nullable = false, num? defaultValue}); - void decimal(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}); + void decimal(String name, + {bool nullable = false, num? defaultValue, int? precision, int? scale}); - void numeric(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}); + void numeric(String name, + {bool nullable = false, num? defaultValue, int? precision, int? scale}); void bit(String name, {bool nullable = false, int? defaultValue}); @@ -170,22 +175,14 @@ abstract class Schema { void make(TableBlueprint table, DBEntityField field) { return switch (field.type) { - const (int) => table.integer( - field.columnName, - nullable: field.nullable, - ), - const (double) || const (num) => table.double( - field.columnName, - nullable: field.nullable, - ), - const (DateTime) => table.datetime( - field.columnName, - nullable: field.nullable, - ), - _ => table.string( - field.columnName, - nullable: field.nullable, - ), + const (int) => + table.integer(field.columnName, nullable: field.nullable), + const (double) || + const (num) => + table.double(field.columnName, nullable: field.nullable), + const (DateTime) => + table.datetime(field.columnName, nullable: field.nullable), + _ => table.string(field.columnName, nullable: field.nullable), }; } @@ -197,15 +194,29 @@ abstract class Schema { autoIncrement: entity.primaryKey.autoIncrement, ); - for (final prop in entity.columns.where((e) => !e.primaryKey)) { + for (final prop in entity.columns.where((e) => !e.isPrimaryKey)) { make(table, prop); } + + for (final prop in entity.referencedFields) { + final foreignKey = ForeignKey( + entity.tableName, + prop.columnName, + foreignTable: prop.reference.tableName, + foreignTableColumn: prop.reference.primaryKey.columnName, + onUpdate: prop.onUpdate, + onDelete: prop.onDelete, + ); + table.foreign(foreignKey); + } + return table; }, ); } - static Schema create(String name, TableBluePrintFunc func) => CreateSchema._(name, func); + static Schema create(String name, TableBluePrintFunc func) => + CreateSchema._(name, func); static Schema dropIfExists(CreateSchema value) { return _DropSchema(value.tableName); @@ -227,39 +238,13 @@ abstract class Migration { } final class CreateSchema extends Schema { - final List _foreignKeys; - - CreateSchema._(super.name, super.func) - : _foreignKeys = [], - super._(); + CreateSchema._(super.name, super.func) : super._(); @override String toScript(TableBlueprint table) { table = _bluePrintFunc!.call(table); - - for (final key in _foreignKeys) { - table.foreign(key); - } return table.createScript(tableName); } - - void foreign({ - String? column, - ForeignKey Function(ForeignKey fkey)? onKey, - }) { - final referenceColumn = column ?? '${ReferenceModel.toString().camelCase}Id'; - - final referenceTable = getEntityTableName(); - final referenceTablePrimaryKey = getEntityPrimaryKey(); - final fkey = ForeignKey( - tableName, - referenceColumn, - foreignTable: referenceTable, - foreignTableColumn: referenceTablePrimaryKey, - ); - onKey?.call(fkey); - _foreignKeys.add(fkey); - } } class _DropSchema extends Schema { @@ -275,7 +260,8 @@ class _RenameSchema extends Schema { _RenameSchema(String from, this.newName) : super._(from, null); @override - String toScript(TableBlueprint table) => table.renameScript(tableName, newName); + String toScript(TableBlueprint table) => + table.renameScript(tableName, newName); } enum ForeignKeyAction { cascade, restrict, setNull, setDefault, noAction } @@ -328,6 +314,7 @@ class ForeignKey { nullable: nullable, onUpdate: onUpdate, onDelete: onDelete, - constraint: name ?? 'fk_${table}_${column}_to_${foreignTable}_$foreignTableColumn', + constraint: name ?? + 'fk_${table}_${column}_to_${foreignTable}_$foreignTableColumn', ); } diff --git a/packages/yaroorm/lib/src/query/query.dart b/packages/yaroorm/lib/src/query/query.dart index ae35501f..908d20b4 100644 --- a/packages/yaroorm/lib/src/query/query.dart +++ b/packages/yaroorm/lib/src/query/query.dart @@ -1,9 +1,10 @@ import 'package:meta/meta.dart'; -import 'package:yaroorm/src/query/aggregates.dart'; -import 'package:yaroorm/yaroorm.dart'; +import '../database/driver/driver.dart'; +import '../database/entity/entity.dart'; import '../primitives/where.dart'; import '../reflection.dart'; +import 'aggregates.dart'; part 'query_impl.dart'; @@ -79,10 +80,8 @@ abstract interface class Query extends QueryBase> final List> whereClauses; final DBEntity entity; - Map get converters => combineConverters( - entity.converters, - runner.typeconverters, - ); + Map get converters => + combineConverters(entity.converters, runner.typeconverters); static final Map _typedatas = {}; diff --git a/packages/yaroorm/lib/src/query/query_impl.dart b/packages/yaroorm/lib/src/query/query_impl.dart index 35b49e98..ce711602 100644 --- a/packages/yaroorm/lib/src/query/query_impl.dart +++ b/packages/yaroorm/lib/src/query/query_impl.dart @@ -31,6 +31,22 @@ class QueryImpl extends Query { @override Future insert(Map data) async { + final typeData = Query.getEntity(); + + if (typeData.timestampsEnabled) { + final now = DateTime.now(); + final createdAtField = typeData.createdAtField; + final updatedAtField = typeData.updatedAtField; + + if (createdAtField != null) { + data[createdAtField.dartName] = now; + } + + if (updatedAtField != null) { + data[updatedAtField.dartName] = now; + } + } + final dataToDbD = conformToDbTypes(data, converters); final recordId = await runner.insert(InsertQuery(tableName, data: dataToDbD)); diff --git a/packages/yaroorm/lib/src/reflection.dart b/packages/yaroorm/lib/src/reflection.dart index 265beaf6..10fbe44f 100644 --- a/packages/yaroorm/lib/src/reflection.dart +++ b/packages/yaroorm/lib/src/reflection.dart @@ -1,7 +1,7 @@ - import 'package:yaroorm/src/utils.dart'; import 'database/entity/entity.dart'; +import 'migration.dart'; import 'query/query.dart'; String getEntityTableName() => Query.getEntity().tableName; @@ -33,7 +33,8 @@ final class DBEntity { final bool timestampsEnabled; - PrimaryKeyField get primaryKey => columns.firstWhereOrNull((e) => e is PrimaryKeyField) as PrimaryKeyField; + PrimaryKeyField get primaryKey => + columns.firstWhereOrNull((e) => e is PrimaryKeyField) as PrimaryKeyField; CreatedAtField? get createdAtField => !timestampsEnabled ? null @@ -43,8 +44,11 @@ final class DBEntity { ? null : columns.firstWhereOrNull((e) => e is UpdatedAtField) as UpdatedAtField?; - List get editableColumns => - columns.where((e) => e != primaryKey).toList(); + Iterable get referencedFields => + columns.whereType(); + + Iterable get editableColumns => + columns.where((e) => e != primaryKey); const DBEntity( this.tableName, { @@ -56,9 +60,6 @@ final class DBEntity { }); } - - - final class DBEntityField { /// dart name for property on Entity class final Symbol dartName; @@ -71,16 +72,53 @@ final class DBEntityField { final bool nullable; - bool get primaryKey => false; + bool get isPrimaryKey => false; - const DBEntityField(this.columnName, this.type, this.dartName, - {this.nullable = false}); + const DBEntityField( + this.columnName, + this.type, + this.dartName, { + this.nullable = false, + }); + + static PrimaryKeyField primaryKey( + String columnName, + Type type, + Symbol dartName, { + bool? autoIncrement, + }) { + return PrimaryKeyField._( + columnName, + type, + dartName, + autoIncrement: autoIncrement ?? false, + ); + } + + static CreatedAtField createdAt(String columnName, Symbol dartName) { + return CreatedAtField._(columnName, dartName); + } + + static UpdatedAtField updatedAt(String columnName, Symbol dartName) { + return UpdatedAtField._(columnName, dartName); + } + + static ReferencedField referenced( + String columnName, + Symbol dartName, { + bool nullable = false, + ForeignKeyAction? onUpdate, + ForeignKeyAction? onDelete, + }) { + return ReferencedField._(columnName, dartName, + nullable: nullable, onUpdate: onUpdate, onDelete: onDelete); + } } final class PrimaryKeyField extends DBEntityField { final bool autoIncrement; - const PrimaryKeyField( + const PrimaryKeyField._( super.columnName, super.type, super.dartName, { @@ -88,15 +126,48 @@ final class PrimaryKeyField extends DBEntityField { }) : super(nullable: false); @override - bool get primaryKey => true; + bool get isPrimaryKey => true; } final class CreatedAtField extends DBEntityField { - const CreatedAtField(String columnName, Symbol dartName) + const CreatedAtField._(String columnName, Symbol dartName) : super(columnName, DateTime, dartName); } final class UpdatedAtField extends DBEntityField { - const UpdatedAtField(String columnName, Symbol dartName) + const UpdatedAtField._(String columnName, Symbol dartName) : super(columnName, DateTime, dartName); } + +final class ReferencedField implements DBEntityField { + final DBEntity reference; + final String _columnName; + final Symbol _dartName; + final bool _nullable; + + final ForeignKeyAction? onUpdate, onDelete; + + ReferencedField._( + this._columnName, + this._dartName, { + bool nullable = false, + this.onDelete, + this.onUpdate, + }) : _nullable = nullable, + reference = Query.getEntity(); + + @override + String get columnName => _columnName; + + @override + Symbol get dartName => _dartName; + + @override + bool get isPrimaryKey => false; + + @override + bool get nullable => _nullable; + + @override + Type get type => reference.primaryKey.type; +} From c3e23ee2d57de55f5352f394975471e66ef0d763 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 7 Apr 2024 17:18:06 +0000 Subject: [PATCH 18/50] tiny fix --- packages/yaroo_cli/lib/orm/_misc.dart | 3 ++- packages/yaroo_cli/lib/orm/commands/command.dart | 3 +++ packages/yaroo_cli/lib/src/migration.g.dart | 2 +- packages/yaroo_cli/pubspec.yaml | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/yaroo_cli/lib/orm/_misc.dart b/packages/yaroo_cli/lib/orm/_misc.dart index e21c03c0..20c90d69 100644 --- a/packages/yaroo_cli/lib/orm/_misc.dart +++ b/packages/yaroo_cli/lib/orm/_misc.dart @@ -2,7 +2,8 @@ import 'package:yaroo_cli/src/migration.dart'; import 'package:yaroorm/yaroorm.dart'; // ignore: non_constant_identifier_names -final MigrationQuery = DB.query(DB.config.migrationsTable); +Query get MigrationQuery => + DB.query(DB.config.migrationsTable); Future ensureMigrationsTableReady(DatabaseDriver driver) async { final hasTable = await driver.hasTable(DB.config.migrationsTable); diff --git a/packages/yaroo_cli/lib/orm/commands/command.dart b/packages/yaroo_cli/lib/orm/commands/command.dart index 183247b8..0a6f8f21 100644 --- a/packages/yaroo_cli/lib/orm/commands/command.dart +++ b/packages/yaroo_cli/lib/orm/commands/command.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:args/command_runner.dart'; +import 'package:yaroo_cli/src/migration.dart'; import 'package:yaroorm/yaroorm.dart'; class MigrationDefn { @@ -48,6 +49,8 @@ abstract class OrmCommand extends Command { @override FutureOr run() async { + Query.addTypeDef(migration_entityTypeData); + final driver = DB.driver(dbConnection); await driver.connect(); diff --git a/packages/yaroo_cli/lib/src/migration.g.dart b/packages/yaroo_cli/lib/src/migration.g.dart index 9fede7be..882fa4a0 100644 --- a/packages/yaroo_cli/lib/src/migration.g.dart +++ b/packages/yaroo_cli/lib/src/migration.g.dart @@ -15,7 +15,7 @@ DBEntity get migration_entityTypeData => "migrations", timestampsEnabled: false, columns: [ - PrimaryKeyField("id", int, #id), + DBEntityField.primaryKey("id", int, #id), DBEntityField("migration", String, #migration), DBEntityField("batch", int, #batch) ], diff --git a/packages/yaroo_cli/pubspec.yaml b/packages/yaroo_cli/pubspec.yaml index a024f00e..f1d90e31 100644 --- a/packages/yaroo_cli/pubspec.yaml +++ b/packages/yaroo_cli/pubspec.yaml @@ -21,6 +21,7 @@ dev_dependencies: test: ^1.24.0 lints: ^3.0.0 build_runner: + yaroorm_builder: executables: yaroo: From dd4da2b93ff6c43deb4b3beb24f4f382bfab12de Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 7 Apr 2024 17:18:21 +0000 Subject: [PATCH 19/50] more fixes --- packages/yaroorm/pubspec.yaml | 5 +++-- .../yaroorm/test/integration/fixtures/migrations.dart | 6 +----- packages/yaroorm/test/models/models.dart | 8 ++++++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/yaroorm/pubspec.yaml b/packages/yaroorm/pubspec.yaml index c402b1a6..57bba0a0 100644 --- a/packages/yaroorm/pubspec.yaml +++ b/packages/yaroorm/pubspec.yaml @@ -25,13 +25,14 @@ dependencies: recase: ^4.1.0 copy_with_extension: ^5.0.4 + dev_dependencies: - yaroorm_builder: lints: ^3.0.0 test: ^1.24.0 yaroo_cli: path: ../yaroo_cli path: ^1.9.0 copy_with_extension_gen: ^5.0.4 - build_runner: collection: ^1.18.0 + build_runner: + yaroorm_builder: diff --git a/packages/yaroorm/test/integration/fixtures/migrations.dart b/packages/yaroorm/test/integration/fixtures/migrations.dart index 85c39efd..c38f551b 100644 --- a/packages/yaroorm/test/integration/fixtures/migrations.dart +++ b/packages/yaroorm/test/integration/fixtures/migrations.dart @@ -17,11 +17,7 @@ class AddUsersTable extends Migration { class AddPostsTable extends Migration { @override void up(List schemas) { - final postSchema = PostSchema - ..foreign( - onKey: (key) => key.actions( - onUpdate: ForeignKeyAction.cascade, - onDelete: ForeignKeyAction.cascade)); + final postSchema = PostSchema; schemas.addAll([postSchema, PostCommentSchema]); } diff --git a/packages/yaroorm/test/models/models.dart b/packages/yaroorm/test/models/models.dart index 7a789944..7fa94932 100644 --- a/packages/yaroorm/test/models/models.dart +++ b/packages/yaroorm/test/models/models.dart @@ -1,3 +1,5 @@ +import 'package:yaroorm/src/database/driver/pgsql_driver.dart'; +import 'package:yaroorm/src/database/driver/sqlite_driver.dart'; import 'package:yaroorm/yaroorm.dart'; part 'models.g.dart'; @@ -33,6 +35,12 @@ class Post extends Entity { final String title; final String description; + @reference( + User, + name: 'user_id', + onUpdate: ForeignKeyAction.cascade, + onDelete: ForeignKeyAction.cascade, + ) final int userId; @createdAtCol From 25bb10ae60d5e0f22ab9ddc69249126f15a53b44 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 01:08:01 +0000 Subject: [PATCH 20/50] more improvements towards type-safety --- packages/yaroo_cli/lib/orm/_misc.dart | 7 +- packages/yaroo_cli/lib/src/migration.g.dart | 41 ++++---- packages/yaroorm/lib/src/config.dart | 6 +- .../lib/src/database/entity/converter.dart | 59 ++++++++--- .../lib/src/database/entity/entity.dart | 36 ++++++- packages/yaroorm/lib/src/migration.dart | 31 ++---- packages/yaroorm/lib/src/query/query.dart | 18 ++-- .../yaroorm/lib/src/query/query_impl.dart | 99 ++++++++++--------- packages/yaroorm/lib/yaroorm.dart | 5 +- packages/yaroorm/pubspec.yaml | 2 - 10 files changed, 175 insertions(+), 129 deletions(-) diff --git a/packages/yaroo_cli/lib/orm/_misc.dart b/packages/yaroo_cli/lib/orm/_misc.dart index 20c90d69..1f059372 100644 --- a/packages/yaroo_cli/lib/orm/_misc.dart +++ b/packages/yaroo_cli/lib/orm/_misc.dart @@ -2,8 +2,7 @@ import 'package:yaroo_cli/src/migration.dart'; import 'package:yaroorm/yaroorm.dart'; // ignore: non_constant_identifier_names -Query get MigrationQuery => - DB.query(DB.config.migrationsTable); +Query get MigrationQuery => DB.query(DB.config.migrationsTable); Future ensureMigrationsTableReady(DatabaseDriver driver) async { final hasTable = await driver.hasTable(DB.config.migrationsTable); @@ -17,9 +16,7 @@ Future hasAlreadyMigratedScript( String scriptName, DatabaseDriver driver, ) async { - final result = await MigrationQuery.driver(driver) - .equal('migration', scriptName) - .findOne(); + final result = await MigrationQuery.driver(driver).Migration(scriptName).findOne(); return result != null; } diff --git a/packages/yaroo_cli/lib/src/migration.g.dart b/packages/yaroo_cli/lib/src/migration.g.dart index 882fa4a0..a922e385 100644 --- a/packages/yaroo_cli/lib/src/migration.g.dart +++ b/packages/yaroo_cli/lib/src/migration.g.dart @@ -10,8 +10,7 @@ part of 'migration.dart'; Query get MigrationEntityQuery => DB.query(); CreateSchema get MigrationEntitySchema => Schema.fromEntity(); -DBEntity get migration_entityTypeData => - DBEntity( +DBEntity get migration_entityTypeData => DBEntity( "migrations", timestampsEnabled: false, columns: [ @@ -42,30 +41,28 @@ class _$MigrationEntityEntityMirror extends EntityMirror { } extension MigrationEntityQueryExtension on Query { + WhereClause Id(int value) => equal("id", value); + WhereClause Migration(String value) => equal("migration", value); + WhereClause Batch(int value) => equal("batch", value); + Future findById(int value) => Id(value).findOne(); + Future findByMigration(String value) => Migration(value).findOne(); + Future findByBatch(int value) => Batch(value).findOne(); Future create({ required String migration, required int batch, - }) => - insert({ - #migration: migration, - #batch: batch, - }); + }) { + return $insert({#migration: migration, #batch: batch}); + } +} + +extension MigrationEntityUpdateQueryExtension on WhereClause { Future update({ - required WhereBuilder where, - required MigrationEntity value, + value migration = const NoValue(), + value batch = const NoValue(), }) async { - final mirror = migration_entityTypeData.mirror(value); - final props = { - for (final column in migration_entityTypeData.columns) - column.dartName: mirror.get(column.dartName), - }; - - final update = UpdateQuery( - entity.tableName, - whereClause: where(this), - data: conformToDbTypes(props, converters), - ); - - await accept(update); + await query.$update(where: (_) => this, values: { + if (migration is! NoValue) #migration: migration.val, + if (batch is! NoValue) #batch: batch.val, + }).execute(); } } diff --git a/packages/yaroorm/lib/src/config.dart b/packages/yaroorm/lib/src/config.dart index 935966aa..dbfc72e6 100644 --- a/packages/yaroorm/lib/src/config.dart +++ b/packages/yaroorm/lib/src/config.dart @@ -8,8 +8,7 @@ class YaroormConfig { final String migrationsTable; final List migrations; - DatabaseConnection get defaultDBConn => - connections.firstWhere((e) => e.name == defaultConnName); + DatabaseConnection get defaultDBConn => connections.firstWhere((e) => e.name == defaultConnName); YaroormConfig( this.defaultConnName, { @@ -19,8 +18,7 @@ class YaroormConfig { }) { final hasDefault = connections.any((e) => e.name == defaultConnName); if (!hasDefault) { - throw ArgumentError( - 'Database connection info not found for $defaultConnName'); + throw ArgumentError('Database connection info not found for $defaultConnName'); } } } diff --git a/packages/yaroorm/lib/src/database/entity/converter.dart b/packages/yaroorm/lib/src/database/entity/converter.dart index 45aea068..463e5387 100644 --- a/packages/yaroorm/lib/src/database/entity/converter.dart +++ b/packages/yaroorm/lib/src/database/entity/converter.dart @@ -46,29 +46,59 @@ Map combineConverters( List driverProvided, ) { return { - for (final converter in [...custom, ...driverProvided]) - converter._dartType: converter, + for (final converter in [...custom, ...driverProvided]) converter._dartType: converter, }; } -Map conformToDbTypes( - Map data, - Map converters, -) { - final entity = Query.getEntity(); +UnmodifiableMapView entityToDbData(Model value) { + final entity = Query.getEntity(type: value.runtimeType); + final typeConverters = combineConverters(entity.converters, value._driver.typeconverters); + final mirror = entity.mirror(value); - Object? toDbType(DBEntityField field) { - final value = data[field.dartName]; - final typeConverter = converters[field.type]; + Object? getValue(DBEntityField field) { + final value = mirror.get(field.dartName); + final typeConverter = typeConverters[field.type]; return typeConverter == null ? value : typeConverter.toDbType(value); } - return { - for (final entry in entity.editableColumns) - entry.columnName: toDbType(entry), + final data = { + for (final entry in entity.editableColumns) entry.columnName: getValue(entry), }; + + return UnmodifiableMapView(data); +} + +@internal +UnmodifiableMapView entityMapToDbData( + Map values, + Map typeConverters, { + bool onlyPropertiesPassed = false, +}) { + final entity = Query.getEntity(); + final editableFields = entity.editableColumns; + + final resultsMap = {}; + + final fieldsToWorkWith = !onlyPropertiesPassed + ? editableFields + : values.keys.map((key) => editableFields.firstWhere((field) => field.dartName == key)); + + for (final field in fieldsToWorkWith) { + var value = values[field.dartName]; + + final typeConverter = typeConverters[field.type]; + value = typeConverter == null ? value : typeConverter.toDbType(value); + if (!field.nullable && value == null) { + throw Exception('Null Value not allowed for Field ${field.dartName} on $T Entity'); + } + + resultsMap[field.columnName] = value; + } + + return UnmodifiableMapView(resultsMap); } +@internal Model serializedPropsToEntity( Map dataFromDb, DBEntity entity, @@ -78,8 +108,7 @@ Model serializedPropsToEntity( for (final entry in entity.columns) { final value = dataFromDb[entry.columnName]; final typeConverter = converters[entry.type]; - resultsMap[entry.dartName] = - typeConverter == null ? value : typeConverter.fromDbType(value); + resultsMap[entry.dartName] = typeConverter == null ? value : typeConverter.fromDbType(value); } return entity.build(resultsMap); diff --git a/packages/yaroorm/lib/src/database/entity/entity.dart b/packages/yaroorm/lib/src/database/entity/entity.dart index 57065756..9673b5cb 100644 --- a/packages/yaroorm/lib/src/database/entity/entity.dart +++ b/packages/yaroorm/lib/src/database/entity/entity.dart @@ -1,4 +1,8 @@ -import 'package:copy_with_extension/copy_with_extension.dart'; +// ignore_for_file: camel_case_types + +import 'dart:collection'; + +import 'package:meta/meta.dart'; import 'package:recase/recase.dart'; import 'package:meta/meta_meta.dart'; @@ -6,6 +10,8 @@ import 'package:meta/meta_meta.dart'; import '../../migration.dart'; import '../../query/query.dart'; import '../../reflection.dart'; +import '../database.dart'; +import '../driver/driver.dart'; part 'converter.dart'; // part 'relations.dart'; @@ -15,6 +21,21 @@ abstract class Entity { Type get _type => runtimeType; + DriverContract _driver = DB.defaultDriver; + + String? get connection => null; + + // ignore: non_constant_identifier_names + UnmodifiableMapView get to_db_data => entityToDbData(this); + + Entity() { + if (connection != null) _driver = DB.driver(connection!); + } + + withDriver(DriverContract driver) { + return this.._driver = driver; + } + // HasOne hasOne({String? foreignKey}) { // return HasOne(foreignKey ?? _foreignKeyForModel, this); // } @@ -25,7 +46,7 @@ abstract class Entity { } @Target({TargetKind.classType}) -class Table extends CopyWith { +class Table { final String name; final List converters; @@ -60,8 +81,6 @@ class UpdatedAtColumn extends TableColumn { } /// Use this to reference other entities -/// -/// ignore: camel_case_types class reference extends TableColumn { final Type type; @@ -73,3 +92,12 @@ class reference extends TableColumn { const primaryKey = PrimaryKey(); const createdAtCol = CreatedAtColumn(); const updatedAtCol = UpdatedAtColumn(); + +class value { + final T? val; + const value(this.val); +} + +class NoValue extends value { + const NoValue() : super(null); +} diff --git a/packages/yaroorm/lib/src/migration.dart b/packages/yaroorm/lib/src/migration.dart index 500d2202..332926bd 100644 --- a/packages/yaroorm/lib/src/migration.dart +++ b/packages/yaroorm/lib/src/migration.dart @@ -36,11 +36,9 @@ abstract class TableBlueprint { void integer(String name, {bool nullable = false, num? defaultValue}); - void double(String name, - {bool nullable = false, num? defaultValue, int? precision, int? scale}); + void double(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}); - void float(String name, - {bool nullable = false, num? defaultValue, int? precision, int? scale}); + void float(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}); void tinyInt(String name, {bool nullable = false, num? defaultValue}); @@ -50,11 +48,9 @@ abstract class TableBlueprint { void bigInteger(String name, {bool nullable = false, num? defaultValue}); - void decimal(String name, - {bool nullable = false, num? defaultValue, int? precision, int? scale}); + void decimal(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}); - void numeric(String name, - {bool nullable = false, num? defaultValue, int? precision, int? scale}); + void numeric(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}); void bit(String name, {bool nullable = false, int? defaultValue}); @@ -175,13 +171,9 @@ abstract class Schema { void make(TableBlueprint table, DBEntityField field) { return switch (field.type) { - const (int) => - table.integer(field.columnName, nullable: field.nullable), - const (double) || - const (num) => - table.double(field.columnName, nullable: field.nullable), - const (DateTime) => - table.datetime(field.columnName, nullable: field.nullable), + const (int) => table.integer(field.columnName, nullable: field.nullable), + const (double) || const (num) => table.double(field.columnName, nullable: field.nullable), + const (DateTime) => table.datetime(field.columnName, nullable: field.nullable), _ => table.string(field.columnName, nullable: field.nullable), }; } @@ -215,8 +207,7 @@ abstract class Schema { ); } - static Schema create(String name, TableBluePrintFunc func) => - CreateSchema._(name, func); + static Schema create(String name, TableBluePrintFunc func) => CreateSchema._(name, func); static Schema dropIfExists(CreateSchema value) { return _DropSchema(value.tableName); @@ -260,8 +251,7 @@ class _RenameSchema extends Schema { _RenameSchema(String from, this.newName) : super._(from, null); @override - String toScript(TableBlueprint table) => - table.renameScript(tableName, newName); + String toScript(TableBlueprint table) => table.renameScript(tableName, newName); } enum ForeignKeyAction { cascade, restrict, setNull, setDefault, noAction } @@ -314,7 +304,6 @@ class ForeignKey { nullable: nullable, onUpdate: onUpdate, onDelete: onDelete, - constraint: name ?? - 'fk_${table}_${column}_to_${foreignTable}_$foreignTableColumn', + constraint: name ?? 'fk_${table}_${column}_to_${foreignTable}_$foreignTableColumn', ); } diff --git a/packages/yaroorm/lib/src/query/query.dart b/packages/yaroorm/lib/src/query/query.dart index 908d20b4..9dd7e9ef 100644 --- a/packages/yaroorm/lib/src/query/query.dart +++ b/packages/yaroorm/lib/src/query/query.dart @@ -23,9 +23,16 @@ mixin FindOperation { } mixin InsertOperation { - Future insert(Map data); + Future $insert(Map data); - Future insertMany(List> values); + Future $insertMany(List> values); +} + +mixin UpdateOperation { + UpdateQuery $update({ + required WhereClause Function(Query query) where, + required Map values, + }); } mixin LimitOperation { @@ -49,8 +56,7 @@ sealed class QueryBase { DriverContract get runner { if (_queryDriver == null) { - throw StateError( - 'Driver not set for query. Make sure you supply a driver using .driver()'); + throw StateError('Driver not set for query. Make sure you supply a driver using .driver()'); } return _queryDriver!; } @@ -74,14 +80,14 @@ abstract interface class Query extends QueryBase> LimitOperation, OrderByOperation>, InsertOperation, + UpdateOperation, AggregateOperation { final Set fieldSelections; final Set orderByProps; final List> whereClauses; final DBEntity entity; - Map get converters => - combineConverters(entity.converters, runner.typeconverters); + Map get converters => combineConverters(entity.converters, runner.typeconverters); static final Map _typedatas = {}; diff --git a/packages/yaroorm/lib/src/query/query_impl.dart b/packages/yaroorm/lib/src/query/query_impl.dart index ce711602..8ae9cfb2 100644 --- a/packages/yaroorm/lib/src/query/query_impl.dart +++ b/packages/yaroorm/lib/src/query/query_impl.dart @@ -11,8 +11,7 @@ class QueryImpl extends Query { String condition, [ Value? value, ]) { - final newClause = WhereClause.create(this, - value: WhereClauseValue.from(field, condition, value)); + final newClause = WhereClause.create(this, value: WhereClauseValue.from(field, condition, value)); whereClauses.add(newClause); return newClause; } @@ -30,13 +29,11 @@ class QueryImpl extends Query { } @override - Future insert(Map data) async { - final typeData = Query.getEntity(); - - if (typeData.timestampsEnabled) { + Future $insert(Map data) async { + if (entity.timestampsEnabled) { final now = DateTime.now(); - final createdAtField = typeData.createdAtField; - final updatedAtField = typeData.updatedAtField; + final createdAtField = entity.createdAtField; + final updatedAtField = entity.updatedAtField; if (createdAtField != null) { data[createdAtField.dartName] = now; @@ -47,14 +44,34 @@ class QueryImpl extends Query { } } - final dataToDbD = conformToDbTypes(data, converters); - final recordId = - await runner.insert(InsertQuery(tableName, data: dataToDbD)); + final dataToDbD = entityMapToDbData(data, converters); + final recordId = await runner.insert(InsertQuery(tableName, data: dataToDbD)); return (await get(recordId))!; } @override - Future insertMany(List> values) async { + UpdateQuery $update({ + required WhereClause Function(Query query) where, + required Map values, + }) { + if (entity.timestampsEnabled) { + final now = DateTime.now(); + final updatedAtField = entity.updatedAtField; + if (updatedAtField != null) { + values[updatedAtField.dartName] = now; + } + } + + final dataToDbD = entityMapToDbData( + values, + converters, + onlyPropertiesPassed: true, + ); + return UpdateQuery(tableName, whereClause: where(this), data: dataToDbD).driver(runner); + } + + @override + Future $insertMany(List> values) async { throw Exception(); } @@ -91,10 +108,8 @@ class QueryImpl extends Query { } @override - WhereClause orWhere(String field, String condition, - [Value? value]) { - throw StateError( - 'Cannot use `orWhere` directly on a Query you need a WHERE clause first'); + WhereClause orWhere(String field, String condition, [Value? value]) { + throw StateError('Cannot use `orWhere` directly on a Query you need a WHERE clause first'); } @override @@ -106,8 +121,7 @@ class QueryImpl extends Query { final newQuery = QueryImpl(tableName); builder(newQuery); - final newGroup = - WhereClause.create(this, operator: LogicalOperator.OR); + final newGroup = WhereClause.create(this, operator: LogicalOperator.OR); for (final clause in newQuery.whereClauses) { newGroup.children.add((clause.operator, clause)); } @@ -122,8 +136,7 @@ class QueryImpl extends Query { final newQuery = QueryImpl(tableName); builder(newQuery); - final newGroup = - WhereClause.create(this, operator: LogicalOperator.AND); + final newGroup = WhereClause.create(this, operator: LogicalOperator.AND); for (final clause in newQuery.whereClauses) { newGroup.children.add((clause.operator, clause)); } @@ -135,79 +148,72 @@ class QueryImpl extends Query { @override WhereClause equal(String field, Value value) { - final newClause = WhereClause.create(this, - value: - WhereClauseValue(field, (operator: Operator.EQUAL, value: value))); + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.EQUAL, value: value))); whereClauses.add(newClause); return newClause; } @override WhereClause notEqual(String field, Value value) { - final newClause = WhereClause.create(this, - value: WhereClauseValue( - field, (operator: Operator.NOT_EQUAL, value: value))); + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NOT_EQUAL, value: value))); whereClauses.add(newClause); return newClause; } @override WhereClause isIn(String field, List values) { - final newClause = WhereClause.create(this, - value: WhereClauseValue(field, (operator: Operator.IN, value: values))); + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.IN, value: values))); whereClauses.add(newClause); return newClause; } @override WhereClause isNotIn(String field, List values) { - final newClause = WhereClause.create(this, - value: WhereClauseValue( - field, (operator: Operator.NOT_IN, value: values))); + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NOT_IN, value: values))); whereClauses.add(newClause); return newClause; } @override WhereClause isLike(String field, String pattern) { - final newClause = WhereClause.create(this, - value: - WhereClauseValue(field, (operator: Operator.LIKE, value: pattern))); + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.LIKE, value: pattern))); whereClauses.add(newClause); return newClause; } @override WhereClause isNotLike(String field, String pattern) { - final newClause = WhereClause.create(this, - value: WhereClauseValue( - field, (operator: Operator.NOT_LIKE, value: pattern))); + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NOT_LIKE, value: pattern))); whereClauses.add(newClause); return newClause; } @override WhereClause isNull(String field) { - final newClause = WhereClause.create(this, - value: WhereClauseValue(field, (operator: Operator.NULL, value: null))); + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NULL, value: null))); whereClauses.add(newClause); return newClause; } @override WhereClause isNotNull(String field) { - final newClause = WhereClause.create(this, - value: WhereClauseValue( - field, (operator: Operator.NOT_NULL, value: null))); + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NOT_NULL, value: null))); whereClauses.add(newClause); return newClause; } @override WhereClause isBetween(String field, List values) { - final newClause = WhereClause.create(this, - value: WhereClauseValue( - field, (operator: Operator.BETWEEN, value: values))); + final newClause = + WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.BETWEEN, value: values))); whereClauses.add(newClause); return newClause; } @@ -215,8 +221,7 @@ class QueryImpl extends Query { @override WhereClause isNotBetween(String field, List values) { final newClause = WhereClause.create(this, - value: WhereClauseValue( - field, (operator: Operator.NOT_BETWEEN, value: values))); + value: WhereClauseValue(field, (operator: Operator.NOT_BETWEEN, value: values))); whereClauses.add(newClause); return newClause; } diff --git a/packages/yaroorm/lib/yaroorm.dart b/packages/yaroorm/lib/yaroorm.dart index a2581784..c36885e0 100644 --- a/packages/yaroorm/lib/yaroorm.dart +++ b/packages/yaroorm/lib/yaroorm.dart @@ -1,11 +1,10 @@ library; export 'src/query/query.dart'; -export 'src/primitives/where.dart' show WhereBuilder; +export 'src/primitives/where.dart' show WhereClause; export 'src/database/driver/driver.dart'; -export 'src/database/entity/entity.dart'; +export 'src/database/entity/entity.dart' hide entityMapToDbData, entityToDbData, serializedPropsToEntity; export 'src/database/database.dart'; export 'src/config.dart'; export 'src/reflection.dart'; export 'src/migration.dart'; -export 'package:copy_with_extension/copy_with_extension.dart'; diff --git a/packages/yaroorm/pubspec.yaml b/packages/yaroorm/pubspec.yaml index 57bba0a0..f5d9e248 100644 --- a/packages/yaroorm/pubspec.yaml +++ b/packages/yaroorm/pubspec.yaml @@ -23,7 +23,6 @@ dependencies: meta: ^1.11.0 grammer: ^1.0.3 recase: ^4.1.0 - copy_with_extension: ^5.0.4 dev_dependencies: @@ -32,7 +31,6 @@ dev_dependencies: yaroo_cli: path: ../yaroo_cli path: ^1.9.0 - copy_with_extension_gen: ^5.0.4 collection: ^1.18.0 build_runner: yaroorm_builder: From bc43ffb87a21cc7f447f80306a63d9440da6bb92 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 01:09:12 +0000 Subject: [PATCH 21/50] more codegen improvements --- packages/generator/lib/src/generator.dart | 173 +++++++++++++--------- 1 file changed, 100 insertions(+), 73 deletions(-) diff --git a/packages/generator/lib/src/generator.dart b/packages/generator/lib/src/generator.dart index 9d6bc6a4..134540fb 100644 --- a/packages/generator/lib/src/generator.dart +++ b/packages/generator/lib/src/generator.dart @@ -1,5 +1,3 @@ -import 'dart:async'; - import 'package:analyzer/dart/constant/value.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/nullability_suffix.dart'; @@ -22,6 +20,8 @@ Builder generatorFactoryBuilder(BuilderOptions options) => SharedPartBuilder( 'yaroorm', ); +typedef FieldData = ({FieldElement field, ConstantReader reader}); + class YaroormGenerator extends GeneratorForAnnotation { final YaroormOptions globalOptions; @@ -46,13 +46,12 @@ class YaroormGenerator extends GeneratorForAnnotation { TypeChecker _typeChecker(Type type) => TypeChecker.fromRuntime(type); - ({FieldElement field, ConstantReader reader})? _getFieldAnnotationByType( + FieldData? _getFieldAnnotationByType( List fields, Type type, ) { for (final field in fields) { - final result = - _typeChecker(type).firstAnnotationOf(field, throwOnUnresolved: false); + final result = _typeChecker(type).firstAnnotationOf(field, throwOnUnresolved: false); if (result != null) { return (field: field, reader: ConstantReader(result)); } @@ -67,10 +66,8 @@ class YaroormGenerator extends GeneratorForAnnotation { final tableName = annotation.peek('name')!.stringValue; final primaryKey = _getFieldAnnotationByType(fields, entity.PrimaryKey); - final createdAtField = - _getFieldAnnotationByType(fields, entity.CreatedAtColumn)?.field; - final updatedAtField = - _getFieldAnnotationByType(fields, entity.UpdatedAtColumn)?.field; + final createdAtField = _getFieldAnnotationByType(fields, entity.CreatedAtColumn)?.field; + final updatedAtField = _getFieldAnnotationByType(fields, entity.UpdatedAtColumn)?.field; final converters = annotation.peek('converters')!.listValue; @@ -78,28 +75,21 @@ class YaroormGenerator extends GeneratorForAnnotation { throw Exception("$className Entity doesn't have primary key"); } - final autoIncrementPrimaryKey = - primaryKey.reader.peek('autoIncrement')!.boolValue; + final autoIncrementPrimaryKey = primaryKey.reader.peek('autoIncrement')!.boolValue; final timestampsEnabled = (createdAtField ?? updatedAtField) != null; /// other properties aside primarykey, updatedAt and createdAt - final normalFields = fields.where( - (e) => ![createdAtField, updatedAtField, primaryKey.field].contains(e)); + final normalFields = fields.where((e) => ![createdAtField, updatedAtField, primaryKey.field].contains(e)); - final creatableFields = [ - if (!autoIncrementPrimaryKey) primaryKey.field, - ...normalFields - ]; + final creatableFields = [if (!autoIncrementPrimaryKey) primaryKey.field, ...normalFields]; - final primaryConstructor = - classElement.constructors.firstWhereOrNull((e) => e.name == ""); + final primaryConstructor = classElement.constructors.firstWhereOrNull((e) => e.name == ""); if (primaryConstructor == null) { throw '$className Entity does not have a default constructor'; } final fieldNames = fields.map((e) => e.name); - final notAllowedProps = - primaryConstructor.children.where((e) => !fieldNames.contains(e.name)); + final notAllowedProps = primaryConstructor.children.where((e) => !fieldNames.contains(e.name)); if (notAllowedProps.isNotEmpty) { throw Exception( 'These props are not allowed in $className Entity default constructor: ${notAllowedProps.join(', ')}'); @@ -109,8 +99,7 @@ class YaroormGenerator extends GeneratorForAnnotation { final symbol = '#${e.name}'; var columnName = e.name; - final meta = _typeChecker(entity.TableColumn) - .firstAnnotationOf(e, throwOnUnresolved: false); + final meta = _typeChecker(entity.TableColumn).firstAnnotationOf(e, throwOnUnresolved: false); ConstantReader? metaReader; if (meta != null) { @@ -126,8 +115,7 @@ class YaroormGenerator extends GeneratorForAnnotation { '''; if (meta != null) { - final isReferenceField = - _typeChecker(entity.reference).isExactly(meta.type!.element!); + final isReferenceField = _typeChecker(entity.reference).isExactly(meta.type!.element!); if (isReferenceField) { final referencedType = metaReader!.peek('type')!.typeValue; @@ -233,57 +221,56 @@ return switch(field) { ''')), ]), ), + Extension( + (b) => b + ..name = '${className}QueryExtension' + ..on = refer('Query<$className>') + ..methods.addAll([ + _generateFieldWhereClause(primaryKey.field, className), + ...normalFields.map((e) => _generateFieldWhereClause(e, className)), + _generateGetByPropertyMethod(primaryKey.field, className), + ...normalFields.map((e) => _generateGetByPropertyMethod(e, className)), + Method( + (m) => m + ..name = 'create' + ..returns = refer('Future<$className>') + ..optionalParameters.addAll(creatableFields.map( + (field) => Parameter((p) => p + ..name = field.name + ..named = true + ..type = refer('${field.type}') + ..required = !field.type.isNullable), + )) + ..body = Code('''return \$insert({ + ${creatableFields.map((e) => '#${e.name}: ${e.name}').join(',')} + });'''), + ), + ]), + ), Extension((b) => b - ..name = '${className}QueryExtension' - ..on = refer('Query<$className>') + ..name = '${className}UpdateQueryExtension' + ..on = refer('WhereClause<$className>') ..methods.addAll([ - Method( - (m) => m - ..name = 'create' - ..returns = refer('Future<$className>') - ..lambda = true - ..optionalParameters.addAll(creatableFields.map( - (field) => Parameter((p) => p - ..name = field.name - ..named = true - ..type = refer('${field.type}') - ..required = !field.type.isNullable), - )) - ..body = Code('''insert({ - ${creatableFields.map((e) => '#${e.name}: ${e.name}').join(',')} - ,})'''), - ), Method( (m) => m ..name = 'update' ..returns = refer('Future') ..modifier = MethodModifier.async - ..optionalParameters.addAll([ - Parameter((p) => p - ..name = 'where' - ..named = true - ..type = refer('WhereBuilder<$className>') - ..required = true), - Parameter((p) => p - ..name = 'value' - ..named = true - ..type = refer(className) - ..required = true) - ]) - ..body = Code(''' - final mirror = $typeDataName.mirror(value); - final props = { - for (final column in $typeDataName.columns) column.dartName: mirror.get(column.dartName), - }; - - final update = UpdateQuery( - entity.tableName, - whereClause: where(this), - data: conformToDbTypes(props, converters), - ); - - await accept(update); -'''), + ..optionalParameters.addAll(normalFields.map( + (field) => Parameter( + (p) => p + ..name = field.name + ..named = true + ..type = refer('value<${field.type.getDisplayString(withNullability: true)}>') + ..defaultTo = Code('const NoValue()') + ..required = false, + ), + )) + ..body = Code('''await query.\$update( + where: (_) => this, + values: { + ${normalFields.map((e) => 'if (${e.name} is! NoValue) #${e.name}: ${e.name}.val').join(',')}, + }).execute();'''), ), ])) ])); @@ -296,8 +283,7 @@ return switch(field) { return field.getter?.isSynthetic ?? false; } - String _generateConstructorCode( - String className, ConstructorElement constructor) { + String _generateConstructorCode(String className, ConstructorElement constructor) { final sb = StringBuffer()..write('$className('); final normalParams = constructor.type.normalParameterNames; @@ -318,11 +304,52 @@ return switch(field) { return (sb..write(')')).toString(); } + /// This generates WHERE-EQUAL Clauses for a field + Method _generateFieldWhereClause(FieldElement field, String className) { + final meta = _typeChecker(entity.TableColumn).firstAnnotationOf(field, throwOnUnresolved: false); + final ConstantReader? reader = meta == null ? null : ConstantReader(meta); + + final fieldName = field.name.pascalCase; + final dbColumnName = (reader?.peek('name')?.stringValue ?? field.name); + final fieldType = field.type.getDisplayString(withNullability: true); + + return Method( + (m) { + m + ..name = fieldName + ..returns = refer('WhereClause<$className>') + ..lambda = true + ..requiredParameters.add(Parameter((p) => p + ..name = 'value' + ..type = refer(fieldType))) + ..body = Code('equal<$fieldType>("$dbColumnName", value)'); + }, + ); + } + + /// This generates GetByProperty for a field + Method _generateGetByPropertyMethod(FieldElement field, String className) { + final fieldName = field.name.pascalCase; + final fieldType = field.type.getDisplayString(withNullability: true); + + return Method( + (m) { + m + ..name = 'findBy$fieldName' + ..returns = refer('Future<$className?>') + ..lambda = true + ..requiredParameters.add(Parameter((p) => p + ..name = 'value' + ..type = refer(fieldType))) + ..body = Code('$fieldName(value).findOne()'); + }, + ); + } + /// Process entity annotation String processAnnotation(DartObject constantValue) { final classElement = constantValue.type!.element as ClassElement; - assert(classElement.supertype!.typeArguments.length == 2, - 'Should have two type arguments'); + assert(classElement.supertype!.typeArguments.length == 2, 'Should have two type arguments'); final variable = constantValue.variable; if (variable != null) return variable.name; From b7cb29ae65162c19efc2fad4915f9a11e4648d7e Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 01:21:44 +0000 Subject: [PATCH 22/50] improve type safety --- packages/generator/lib/src/generator.dart | 4 +- packages/yaroo_cli/lib/orm/_misc.dart | 2 +- packages/yaroo_cli/lib/src/migration.g.dart | 19 +- packages/yaroorm/.gitignore | 3 +- .../yaroorm/test/integration/e2e_basic.dart | 15 +- .../test/integration/fixtures/migrations.dart | 2 +- .../fixtures}/models.dart | 2 - .../test/unit/dialects/sqlite_test.dart | 395 ++++++------------ packages/yaroorm/test/yaroorm_test.dart | 27 +- 9 files changed, 155 insertions(+), 314 deletions(-) rename packages/yaroorm/test/{models => integration/fixtures}/models.dart (90%) diff --git a/packages/generator/lib/src/generator.dart b/packages/generator/lib/src/generator.dart index 134540fb..43960b11 100644 --- a/packages/generator/lib/src/generator.dart +++ b/packages/generator/lib/src/generator.dart @@ -316,7 +316,7 @@ return switch(field) { return Method( (m) { m - ..name = fieldName + ..name = 'where$fieldName' ..returns = refer('WhereClause<$className>') ..lambda = true ..requiredParameters.add(Parameter((p) => p @@ -341,7 +341,7 @@ return switch(field) { ..requiredParameters.add(Parameter((p) => p ..name = 'value' ..type = refer(fieldType))) - ..body = Code('$fieldName(value).findOne()'); + ..body = Code('where$fieldName(value).findOne()'); }, ); } diff --git a/packages/yaroo_cli/lib/orm/_misc.dart b/packages/yaroo_cli/lib/orm/_misc.dart index 1f059372..c978349e 100644 --- a/packages/yaroo_cli/lib/orm/_misc.dart +++ b/packages/yaroo_cli/lib/orm/_misc.dart @@ -16,7 +16,7 @@ Future hasAlreadyMigratedScript( String scriptName, DatabaseDriver driver, ) async { - final result = await MigrationQuery.driver(driver).Migration(scriptName).findOne(); + final result = await MigrationQuery.driver(driver).whereMigration(scriptName).findOne(); return result != null; } diff --git a/packages/yaroo_cli/lib/src/migration.g.dart b/packages/yaroo_cli/lib/src/migration.g.dart index a922e385..bafdc093 100644 --- a/packages/yaroo_cli/lib/src/migration.g.dart +++ b/packages/yaroo_cli/lib/src/migration.g.dart @@ -10,7 +10,8 @@ part of 'migration.dart'; Query get MigrationEntityQuery => DB.query(); CreateSchema get MigrationEntitySchema => Schema.fromEntity(); -DBEntity get migration_entityTypeData => DBEntity( +DBEntity get migration_entityTypeData => + DBEntity( "migrations", timestampsEnabled: false, columns: [ @@ -41,12 +42,16 @@ class _$MigrationEntityEntityMirror extends EntityMirror { } extension MigrationEntityQueryExtension on Query { - WhereClause Id(int value) => equal("id", value); - WhereClause Migration(String value) => equal("migration", value); - WhereClause Batch(int value) => equal("batch", value); - Future findById(int value) => Id(value).findOne(); - Future findByMigration(String value) => Migration(value).findOne(); - Future findByBatch(int value) => Batch(value).findOne(); + WhereClause whereId(int value) => equal("id", value); + WhereClause whereMigration(String value) => + equal("migration", value); + WhereClause whereBatch(int value) => + equal("batch", value); + Future findById(int value) => whereId(value).findOne(); + Future findByMigration(String value) => + whereMigration(value).findOne(); + Future findByBatch(int value) => + whereBatch(value).findOne(); Future create({ required String migration, required int batch, diff --git a/packages/yaroorm/.gitignore b/packages/yaroorm/.gitignore index 77483a44..2d7fe2c4 100644 --- a/packages/yaroorm/.gitignore +++ b/packages/yaroorm/.gitignore @@ -6,4 +6,5 @@ # https://dart.dev/guides/libraries/private-files#pubspeclock. pubspec.lock *.sqlite -*.entity.dart \ No newline at end of file +*.entity.dart +*.g.dart \ No newline at end of file diff --git a/packages/yaroorm/test/integration/e2e_basic.dart b/packages/yaroorm/test/integration/e2e_basic.dart index f39c9a07..2fc285a3 100644 --- a/packages/yaroorm/test/integration/e2e_basic.dart +++ b/packages/yaroorm/test/integration/e2e_basic.dart @@ -2,7 +2,7 @@ import 'package:test/test.dart'; import 'package:yaroorm/yaroorm.dart'; import 'package:collection/collection.dart'; -import '../models/models.dart'; +import 'fixtures/models.dart'; import 'fixtures/migrator.dart'; import 'fixtures/test_data.dart'; @@ -17,8 +17,7 @@ void runBasicE2ETest(String connectionName) { expect(_.driver.isOpen, isTrue); }); - test('should have no tables', - () async => expect(await _.driver.hasTable('users'), isFalse)); + test('should have no tables', () async => expect(await _.driver.hasTable('users'), isFalse)); test('should execute migration', () async { await runMigrator(connectionName, 'migrate'); @@ -43,11 +42,8 @@ void runBasicE2ETest(String connectionName) { test('should insert many users', () async { await Future.wait(usersList .sublist(1) - .map((e) => userQuery.create( - firstname: e.firstname, - lastname: e.lastname, - age: e.age, - homeAddress: e.homeAddress)) + .map((e) => + userQuery.create(firstname: e.firstname, lastname: e.lastname, age: e.age, homeAddress: e.homeAddress)) .toList()); expect(await UserQuery.count(), hasLength(usersList.length)); @@ -88,8 +84,7 @@ void runBasicE2ETest(String connectionName) { test('concat', () async { Matcher matcher(String separator) { - if ([DatabaseDriverType.sqlite, DatabaseDriverType.pgsql] - .contains(_.driver.type)) { + if ([DatabaseDriverType.sqlite, DatabaseDriverType.pgsql].contains(_.driver.type)) { return equals(usersInGhana.map((e) => e.age).join(separator)); } diff --git a/packages/yaroorm/test/integration/fixtures/migrations.dart b/packages/yaroorm/test/integration/fixtures/migrations.dart index c38f551b..cdfe5b78 100644 --- a/packages/yaroorm/test/integration/fixtures/migrations.dart +++ b/packages/yaroorm/test/integration/fixtures/migrations.dart @@ -1,6 +1,6 @@ import 'package:yaroorm/src/migration.dart'; -import '../../models/models.dart'; +import 'models.dart'; class AddUsersTable extends Migration { @override diff --git a/packages/yaroorm/test/models/models.dart b/packages/yaroorm/test/integration/fixtures/models.dart similarity index 90% rename from packages/yaroorm/test/models/models.dart rename to packages/yaroorm/test/integration/fixtures/models.dart index 7fa94932..e3dbd23c 100644 --- a/packages/yaroorm/test/models/models.dart +++ b/packages/yaroorm/test/integration/fixtures/models.dart @@ -1,5 +1,3 @@ -import 'package:yaroorm/src/database/driver/pgsql_driver.dart'; -import 'package:yaroorm/src/database/driver/sqlite_driver.dart'; import 'package:yaroorm/yaroorm.dart'; part 'models.g.dart'; diff --git a/packages/yaroorm/test/unit/dialects/sqlite_test.dart b/packages/yaroorm/test/unit/dialects/sqlite_test.dart index fb51cb48..b239721d 100644 --- a/packages/yaroorm/test/unit/dialects/sqlite_test.dart +++ b/packages/yaroorm/test/unit/dialects/sqlite_test.dart @@ -1,6 +1,7 @@ import 'package:test/test.dart'; import 'package:yaroorm/yaroorm.dart'; +import '../../integration/fixtures/models.dart'; import '../../integration/fixtures/orm_config.dart' as db; part 'sqlite_test.g.dart'; @@ -30,35 +31,37 @@ class ArticleComment extends Entity { void main() { DB.init(db.config); + Query.addTypeDef(userTypeData); + Query.addTypeDef
(articleTypeData); + Query.addTypeDef(article_commentTypeData); + Query.addTypeDef(postTypeData); + Query.addTypeDef(post_commentTypeData); + late DatabaseDriver driver; setUpAll(() => driver = DB.driver('foo_sqlite')); group('SQLITE Query Builder', () { test('when query', () { - final query = Query.table('users').driver(driver); + final query = UserQuery.driver(driver); expect(query.statement, 'SELECT * FROM users;'); }); test('when query with single orderBy', () { - final query = Query.table('users').driver(driver).orderByDesc('names'); + final query = UserQuery.driver(driver).orderByDesc('names'); expect(query.statement, 'SELECT * FROM users ORDER BY names DESC;'); }); test('when query with multiple orderBy', () { - final query = Query.table('users') - .driver(driver) - .orderByDesc('names') - .orderByAsc('ages'); + final query = UserQuery.driver(driver).orderByDesc('names').orderByAsc('ages'); - expect(query.statement, - 'SELECT * FROM users ORDER BY names DESC, ages ASC;'); + expect(query.statement, 'SELECT * FROM users ORDER BY names DESC, ages ASC;'); }); // test('when update', () { - // final query = Query.table('users').driver(driver).update( + // final query = UserQuery.driver(driver).update( // where: (where) => where.where('name', '=', 'Chima'), // values: {'firstname': 'Chima', 'lastname': 'Precious'}, // ); @@ -68,7 +71,7 @@ void main() { // }); // test('when delete', () { - // final query = Query.table('users') + // final query = UserQuery // .driver(driver) // .delete((where) => where.where('name', '=', 'Chima')); @@ -77,30 +80,20 @@ void main() { group('when .where', () { test('of level 1', () { - final query = Query.table('users') - .driver(driver) - .where('firstname', '=', 'Chima'); + final query = UserQuery.driver(driver).where('firstname', '=', 'Chima'); - expect(query.statement, - 'SELECT * FROM users WHERE firstname = \'Chima\';'); + expect(query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\';'); }); test('of level 2', () { - final query = Query.table('users') - .driver(driver) - .where('firstname', '=', 'Chima') - .where('lastname', '=', 'Precious'); + final query = UserQuery.driver(driver).whereFirstname('Chima').where('lastname', '=', 'Precious'); - expect(query.statement, - 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname = \'Precious\';'); + expect(query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname = \'Precious\';'); }); test('of level 3', () { - final query = Query.table('users') - .driver(driver) - .where('firstname', '=', 'Chima') - .where('lastname', '=', 'Precious') - .where('age', '=', 22); + final query = + UserQuery.driver(driver).whereFirstname('Chima').equal('lastname', 'Precious').where('age', '=', 22); expect(query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname = \'Precious\' AND age = 22;'); @@ -108,40 +101,28 @@ void main() { group('chained with `.orWhere`', () { test('of level 1', () { - final query = Query.table('users') - .driver(driver) - .where('firstname', '=', 'Chima') - .orWhere('age', '=', 203); + final query = UserQuery.driver(driver).whereFirstname('Chima').orWhere('age', '=', 203); - expect(query.statement, - 'SELECT * FROM users WHERE firstname = \'Chima\' OR age = 203;'); + expect(query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\' OR age = 203;'); }); test('of level 2', () { - final query = Query.table('users') - .driver(driver) - .where('firstname', '=', 'Chima') - .orWhere('age', '=', 203) - .where('city', '!=', 'Accra'); + final query = + UserQuery.driver(driver).whereFirstname('Chima').orWhere('age', '=', 203).where('city', '!=', 'Accra'); - expect(query.statement, - 'SELECT * FROM users WHERE firstname = \'Chima\' OR (age = 203 AND city != \'Accra\');'); + expect( + query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\' OR (age = 203 AND city != \'Accra\');'); }); test('of level 3', () { - final query = Query.table('users') - .driver(driver) - .where('votes', '>', 100) - .orWhere('name', '=', 'Abigail') - .where('votes', '>', 50); - - expect(query.statement, - 'SELECT * FROM users WHERE votes > 100 OR (name = \'Abigail\' AND votes > 50);'); + final query = + UserQuery.driver(driver).where('votes', '>', 100).orWhere('name', '=', 'Abigail').where('votes', '>', 50); + + expect(query.statement, 'SELECT * FROM users WHERE votes > 100 OR (name = \'Abigail\' AND votes > 50);'); }); test('of level 4', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .where('firstname', '=', 'Chima') .orWhere('age', '=', 203) .orWhere('city', '!=', 'Accra') @@ -154,8 +135,7 @@ void main() { }); test('of level 5', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .where('firstname', '=', 'Chima') .orWhere('age', '=', 203) .orWhere('city', '!=', 'Accra') @@ -171,10 +151,7 @@ void main() { group('chained with', () { test('.whereNull', () { - final query = Query.table('users') - .driver(driver) - .where('firstname', '=', 'Chima') - .isNull('age'); + final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isNull('age'); expect( query.statement, @@ -183,10 +160,7 @@ void main() { }); test('.whereNotNull', () { - final query = Query.table('users') - .driver(driver) - .where('firstname', '=', 'Chima') - .isNotNull('age'); + final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isNotNull('age'); expect( query.statement, @@ -195,10 +169,7 @@ void main() { }); test('.whereIn', () { - final query = Query.table('users') - .driver(driver) - .where('firstname', '=', 'Chima') - .isIn('age', [22, 24, 25]); + final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isIn('age', [22, 24, 25]); expect( query.statement, @@ -207,10 +178,7 @@ void main() { }); test('.whereNotIn', () { - final query = Query.table('users') - .driver(driver) - .where('firstname', '=', 'Chima') - .isNotIn('age', [22, 24, 25]); + final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isNotIn('age', [22, 24, 25]); expect( query.statement, @@ -219,10 +187,7 @@ void main() { }); test('.whereLike', () { - final query = Query.table('users') - .driver(driver) - .where('firstname', '=', 'Chima') - .isLike('lastname', 'hello%'); + final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isLike('lastname', 'hello%'); expect( query.statement, @@ -231,10 +196,7 @@ void main() { }); test('.whereNotLike', () { - final query = Query.table('users') - .driver(driver) - .where('firstname', '=', 'Chima') - .isNotLike('lastname', 'hello%'); + final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isNotLike('lastname', 'hello%'); expect( query.statement, @@ -243,10 +205,7 @@ void main() { }); test('.whereBetween', () { - final query = Query.table('users') - .driver(driver) - .where('firstname', '=', 'Chima') - .isBetween('lastname', [22, 50]); + final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isBetween('lastname', [22, 50]); expect( query.statement, @@ -255,10 +214,8 @@ void main() { }); test('.whereNotBetween', () { - final query = Query.table('users') - .driver(driver) - .where('firstname', '=', 'Chima') - .isNotBetween('lastname', [22.34, 50]); + final query = + UserQuery.driver(driver).where('firstname', '=', 'Chima').isNotBetween('lastname', [22.34, 50]); expect( query.statement, @@ -268,32 +225,20 @@ void main() { }); test('with orderBy', () { - final query = Query.table('users') - .driver(driver) - .where('name', '=', 'Chima') - .orderByDesc('names') - .orderByAsc('ages'); + final query = UserQuery.driver(driver).where('name', '=', 'Chima').orderByDesc('names').orderByAsc('ages'); - expect(query.statement, - 'SELECT * FROM users WHERE name = \'Chima\' ORDER BY names DESC, ages ASC;'); + expect(query.statement, 'SELECT * FROM users WHERE name = \'Chima\' ORDER BY names DESC, ages ASC;'); }); }); group('when handwritten operator', () { test('should error if unknown operator', () { - expect( - () => Query.table('users') - .driver(driver) - .where('age', 'foo-bar', '23') - .statement, - throwsA(isA().having( - (p0) => p0.message, '', 'Condition foo-bar is not known'))); + expect(() => UserQuery.driver(driver).where('age', 'foo-bar', '23').statement, + throwsA(isA().having((p0) => p0.message, '', 'Condition foo-bar is not known'))); }); test('=', () { - final query = Query.table('users') - .driver(driver) - .where('firstname', '=', 'Chima'); + final query = UserQuery.driver(driver).where('firstname', '=', 'Chima'); expect( query.statement, @@ -302,44 +247,37 @@ void main() { }); test('!=', () { - final query = Query.table('users') - .driver(driver) - .where('firstname', '!=', 'Chima'); + final query = UserQuery.driver(driver).where('firstname', '!=', 'Chima'); - expect(query.statement, - 'SELECT * FROM users WHERE firstname != \'Chima\';'); + expect(query.statement, 'SELECT * FROM users WHERE firstname != \'Chima\';'); }); test('>', () { - final query = Query.table('users').driver(driver).where('age', '>', 23); + final query = UserQuery.driver(driver).where('age', '>', 23); expect(query.statement, 'SELECT * FROM users WHERE age > 23;'); }); test('<', () { - final query = Query.table('users').driver(driver).where('age', '<', 23); + final query = UserQuery.driver(driver).where('age', '<', 23); expect(query.statement, 'SELECT * FROM users WHERE age < 23;'); }); test('>=', () { - final query = - Query.table('users').driver(driver).where('age', '>=', 223); + final query = UserQuery.driver(driver).where('age', '>=', 223); expect(query.statement, 'SELECT * FROM users WHERE age >= 223;'); }); test('<=', () { - final query = - Query.table('users').driver(driver).where('age', '<=', 34.3); + final query = UserQuery.driver(driver).where('age', '<=', 34.3); expect(query.statement, 'SELECT * FROM users WHERE age <= 34.3;'); }); test('in', () { - final query = Query.table('users') - .driver(driver) - .where('places', 'in', ['Accra', 'Tema']); + final query = UserQuery.driver(driver).where('places', 'in', ['Accra', 'Tema']); expect( query.statement, @@ -348,9 +286,7 @@ void main() { }); test('not in', () { - final query = Query.table('users') - .driver(driver) - .where('places', 'not in', ['Accra', 'Tema']); + final query = UserQuery.driver(driver).where('places', 'not in', ['Accra', 'Tema']); expect( query.statement, @@ -359,8 +295,7 @@ void main() { }); test('null', () { - final query = - Query.table('users').driver(driver).where('places', 'null'); + final query = UserQuery.driver(driver).where('places', 'null'); expect( query.statement, @@ -369,8 +304,7 @@ void main() { }); test('not null', () { - final query = - Query.table('users').driver(driver).where('places', 'not null'); + final query = UserQuery.driver(driver).where('places', 'not null'); expect( query.statement, @@ -379,9 +313,7 @@ void main() { }); test('like', () { - final query = Query.table('users') - .driver(driver) - .where('places', 'like', "MerryC"); + final query = UserQuery.driver(driver).where('places', 'like', "MerryC"); expect( query.statement, @@ -390,9 +322,7 @@ void main() { }); test('not like', () { - final query = Query.table('users') - .driver(driver) - .where('places', 'not like', "MerryC"); + final query = UserQuery.driver(driver).where('places', 'not like', "MerryC"); expect( query.statement, @@ -401,9 +331,7 @@ void main() { }); test('between', () { - final query = Query.table('users') - .driver(driver) - .where('age', 'between', [22, 30]); + final query = UserQuery.driver(driver).where('age', 'between', [22, 30]); expect( query.statement, @@ -412,9 +340,7 @@ void main() { }); test('not between', () { - final query = Query.table('users') - .driver(driver) - .where('age', 'not between', [22, 30]); + final query = UserQuery.driver(driver).where('age', 'not between', [22, 30]); expect( query.statement, @@ -425,9 +351,7 @@ void main() { group('when .whereIn', () { test('of level 1', () { - final query = Query.table('users') - .driver(driver) - .isIn('firstname', ['Accra', 'Tamale']); + final query = UserQuery.driver(driver).isIn('firstname', ['Accra', 'Tamale']); expect( query.statement, @@ -436,8 +360,7 @@ void main() { }); test('of level 2', () { - final query = Query.table('users').driver(driver).isIn( - 'places', ['Accra', 'Tamale']).where('lastname', '=', 'Precious'); + final query = UserQuery.driver(driver).isIn('places', ['Accra', 'Tamale']).where('lastname', '=', 'Precious'); expect( query.statement, @@ -446,8 +369,7 @@ void main() { }); test('of level 3', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .isIn('places', ['Accra', 'Tamale']) .where('lastname', '=', 'Precious') .where('names', 'like', 'Hello%'); @@ -459,8 +381,7 @@ void main() { }); test('of level 4', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .isIn('places', ['Accra', 'Tamale']) .where('lastname', '=', 'Precious') .where('names', 'like', 'Hello%') @@ -475,9 +396,7 @@ void main() { group('when .whereNotIn', () { test('of level 1', () { - final query = Query.table('users') - .driver(driver) - .isNotIn('firstname', ['Accra', 'Tamale']); + final query = UserQuery.driver(driver).isNotIn('firstname', ['Accra', 'Tamale']); expect( query.statement, @@ -486,8 +405,8 @@ void main() { }); test('of level 2', () { - final query = Query.table('users').driver(driver).isNotIn( - 'places', ['Accra', 'Tamale']).where('lastname', '=', 'Precious'); + final query = + UserQuery.driver(driver).isNotIn('places', ['Accra', 'Tamale']).where('lastname', '=', 'Precious'); expect( query.statement, @@ -496,8 +415,7 @@ void main() { }); test('of level 3', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .isNotIn('places', ['Accra', 'Tamale']) .where('lastname', '=', 'Precious') .isNotNull('names'); @@ -509,8 +427,7 @@ void main() { }); test('of level 4', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .isNotIn('places', ['Accra', 'Tamale']) .where('lastname', '=', 'Precious') .where('names', 'like', 'Hello%') @@ -526,16 +443,13 @@ void main() { group('when .whereBetween', () { test('should error if not supplied List with length 2', () { expect( - () => Query.table('users') - .driver(driver) - .isBetween('age', [22]).statement, - throwsA(isA().having((p0) => p0.message, '', - 'BETWEEN requires a List with length 2 (val1, val2)'))); + () => UserQuery.driver(driver).isBetween('age', [22]).statement, + throwsA(isA() + .having((p0) => p0.message, '', 'BETWEEN requires a List with length 2 (val1, val2)'))); }); test('of level 1', () { - final query = - Query.table('users').driver(driver).isBetween('age', [22, 70]); + final query = UserQuery.driver(driver).isBetween('age', [22, 70]); expect( query.statement, @@ -544,10 +458,8 @@ void main() { }); test('of level 2', () { - final query = Query.table('users') - .driver(driver) - .isBetween('places', ['Accra', 'Tamale']).where( - 'lastname', 'between', [2, 100]); + final query = + UserQuery.driver(driver).isBetween('places', ['Accra', 'Tamale']).where('lastname', 'between', [2, 100]); expect( query.statement, @@ -556,10 +468,8 @@ void main() { }); test('of level 3', () { - final query = Query.table('users') - .driver(driver) - .isIn('places', ['Accra', 'Tamale']).isBetween( - 'lastname', [22, 48]).where('names', 'like', 'Hello%'); + final query = UserQuery.driver(driver) + .isIn('places', ['Accra', 'Tamale']).isBetween('lastname', [22, 48]).where('names', 'like', 'Hello%'); expect( query.statement, @@ -568,8 +478,7 @@ void main() { }); test('of level 4', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .isIn('places', ['Accra', 'Tamale']) .where('lastname', '=', 'Precious') .orWhere('age', 'in', [23, 34, 55]) @@ -585,16 +494,13 @@ void main() { group('when .whereNotBetween', () { test('should error if not supplied List with length 2', () { expect( - () => Query.table('users') - .driver(driver) - .isNotBetween('age', [22]).statement, - throwsA(isA().having((p0) => p0.message, '', - 'NOT_BETWEEN requires a List with length 2 (val1, val2)'))); + () => UserQuery.driver(driver).isNotBetween('age', [22]).statement, + throwsA(isA() + .having((p0) => p0.message, '', 'NOT_BETWEEN requires a List with length 2 (val1, val2)'))); }); test('of level 1', () { - final query = - Query.table('users').driver(driver).isNotBetween('age', [22, 70]); + final query = UserQuery.driver(driver).isNotBetween('age', [22, 70]); expect( query.statement, @@ -603,10 +509,8 @@ void main() { }); test('of level 2', () { - final query = Query.table('users') - .driver(driver) - .isNotBetween('places', ['Accra', 'Tamale']).where( - 'lastname', 'between', [2, 100]); + final query = + UserQuery.driver(driver).isNotBetween('places', ['Accra', 'Tamale']).where('lastname', 'between', [2, 100]); expect( query.statement, @@ -615,10 +519,8 @@ void main() { }); test('of level 3', () { - final query = Query.table('users') - .driver(driver) - .isIn('places', ['Accra', 'Tamale']).isNotBetween( - 'lastname', [22, 48]).where('names', 'like', 'Hello%'); + final query = UserQuery.driver(driver) + .isIn('places', ['Accra', 'Tamale']).isNotBetween('lastname', [22, 48]).where('names', 'like', 'Hello%'); expect( query.statement, @@ -627,8 +529,7 @@ void main() { }); test('of level 4', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .isIn('places', ['Accra', 'Tamale']) .where('lastname', '=', 'Precious') .orWhere('age', 'in', [23, 34, 55]) @@ -643,8 +544,7 @@ void main() { group('when .whereLike', () { test('of level 1', () { - final query = - Query.table('users').driver(driver).isLike('firstname', 'Names%%'); + final query = UserQuery.driver(driver).isLike('firstname', 'Names%%'); expect( query.statement, @@ -653,10 +553,7 @@ void main() { }); test('of level 2', () { - final query = Query.table('users') - .driver(driver) - .isLike('places', 'Chima**') - .where('lastname', '=', 'Precious'); + final query = UserQuery.driver(driver).isLike('places', 'Chima**').where('lastname', '=', 'Precious'); expect( query.statement, @@ -665,8 +562,7 @@ void main() { }); test('of level 3', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .isLike('places', 'Hello123') .where('lastname', '=', 'Precious') .where('names', 'like', 'Hello%'); @@ -678,8 +574,7 @@ void main() { }); test('of level 4', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .isLike('places', 'Nems#') .where('lastname', '=', 'Precious') .where('names', 'like', 'Hello%') @@ -694,9 +589,7 @@ void main() { group('when .whereNotLike', () { test('of level 1', () { - final query = Query.table('users') - .driver(driver) - .isNotLike('firstname', 'Names%%'); + final query = UserQuery.driver(driver).isNotLike('firstname', 'Names%%'); expect( query.statement, @@ -705,10 +598,7 @@ void main() { }); test('of level 2', () { - final query = Query.table('users') - .driver(driver) - .isNotLike('places', 'Chima**') - .isBetween('lastname', [12, 90]); + final query = UserQuery.driver(driver).isNotLike('places', 'Chima**').isBetween('lastname', [12, 90]); expect( query.statement, @@ -717,8 +607,7 @@ void main() { }); test('of level 3', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .isNotLike('places', 'Hello123') .where('lastname', '=', 'Precious') .where('names', 'like', 'Hello%'); @@ -730,8 +619,7 @@ void main() { }); test('of level 4', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .isNotLike('places', 'Nems#') .where('lastname', '=', 'Precious') .orWhere('names', 'not like', 'Hello%') @@ -746,16 +634,13 @@ void main() { group('when .whereNull', () { test('of level 1', () { - final query = Query.table('users').driver(driver).isNull('firstname'); + final query = UserQuery.driver(driver).isNull('firstname'); expect(query.statement, 'SELECT * FROM users WHERE firstname IS NULL;'); }); test('of level 2', () { - final query = Query.table('users') - .driver(driver) - .isNull('places') - .where('lastname', '=', 'Precious'); + final query = UserQuery.driver(driver).isNull('places').where('lastname', '=', 'Precious'); expect( query.statement, @@ -764,8 +649,7 @@ void main() { }); test('of level 3', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .isNull('places') .where('lastname', '=', 'Precious') .where('names', 'like', 'Hello%'); @@ -777,8 +661,7 @@ void main() { }); test('of level 4', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .isNull('places') .where('lastname', '=', 'Precious') .orWhere('names', 'null') @@ -793,8 +676,7 @@ void main() { group('when .whereNotNull', () { test('of level 1', () { - final query = - Query.table('users').driver(driver).isNotNull('firstname'); + final query = UserQuery.driver(driver).isNotNull('firstname'); expect( query.statement, @@ -803,10 +685,7 @@ void main() { }); test('of level 2', () { - final query = Query.table('users') - .driver(driver) - .isNotNull('places') - .where('lastname', '=', 'Precious'); + final query = UserQuery.driver(driver).isNotNull('places').where('lastname', '=', 'Precious'); expect( query.statement, @@ -815,8 +694,7 @@ void main() { }); test('of level 3', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .isNotNull('places') .where('lastname', '=', 'Precious') .where('names', 'like', 'Hello%'); @@ -828,8 +706,7 @@ void main() { }); test('of level 4', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .isNotNull('places') .where('lastname', '=', 'Precious') .orWhere('names', 'not null') @@ -843,11 +720,9 @@ void main() { }); test('when .whereFunc', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .where('name', '=', 'John') - .whereFunc((query) => - query.where('votes', '>', 100).orWhere('title', '=', 'Admin')); + .whereFunc((query) => query.where('votes', '>', 100).orWhere('title', '=', 'Admin')); expect( query.statement, @@ -857,11 +732,9 @@ void main() { group('when .orWhereFunc', () { test('of level 1', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .where('votes', '>', 100) - .orWhereFunc((query) => - query.where('name', '=', 'Abigail').where('votes', '>', 50)); + .orWhereFunc((query) => query.where('name', '=', 'Abigail').where('votes', '>', 50)); expect( query.statement, @@ -870,13 +743,10 @@ void main() { }); test('of level 2', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .where('votes', '>', 100) - .orWhereFunc((query) => - query.where('name', '=', 'Abigail').where('votes', '>', 50)) - .orWhereFunc((query) => - query.where('price', '=', 'GHC200').where('votes', 'not null')); + .orWhereFunc((query) => query.where('name', '=', 'Abigail').where('votes', '>', 50)) + .orWhereFunc((query) => query.where('price', '=', 'GHC200').where('votes', 'not null')); expect( query.statement, @@ -885,11 +755,9 @@ void main() { }); test('or level 3', () { - var query = Query.table('users') - .driver(driver) + var query = UserQuery.driver(driver) .where('votes', '>', 100) - .orWhereFunc((query) => - query.where('name', '=', 'Abigail').where('votes', '>', 50)) + .orWhereFunc((query) => query.where('name', '=', 'Abigail').where('votes', '>', 50)) .where('name', '=', 22) .orWhereFunc((query) => query.where('name', '=', 'Abigail')); @@ -904,17 +772,13 @@ void main() { group('when used at start of query', () { test('when .orWhereFunc', () { expect( - () => Query.table('users') - .driver(driver) - .orWhereFunc((query) => query.where('name', '=', 'Abigail')), + () => UserQuery.driver(driver).orWhereFunc((query) => query.where('name', '=', 'Abigail')), throwsStateError, ); }); test('when .whereFunc', () { - var query = Query.table('users') - .driver(driver) - .whereFunc((query) => query.where('name', '=', 'Abigail')); + var query = UserQuery.driver(driver).whereFunc((query) => query.where('name', '=', 'Abigail')); expect( query.statement, @@ -924,11 +788,9 @@ void main() { }); test('when used together', () { - var query = Query.table('users') - .driver(driver) + var query = UserQuery.driver(driver) .whereFunc((query) => query.where('name', '=', 'Abigail')) - .orWhereFunc((query) => - query.where('age', '<', 24).where('names', 'not null')); + .orWhereFunc((query) => query.where('age', '<', 24).where('names', 'not null')); expect( query.statement, @@ -938,10 +800,7 @@ void main() { group('when nested crazy', () { test('with level 1', () { - final query = Query.table('users') - .driver(driver) - .where('name', '=', 'Chima') - .orWhereFunc( + final query = UserQuery.driver(driver).where('name', '=', 'Chima').orWhereFunc( (query) => query .where('biscuit', '=', 'hello-world') .orWhere('car_type', '=', 'lamborgini server') @@ -955,8 +814,7 @@ void main() { }); test('with level 2', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .where('name', '=', 'Chima') .orWhereFunc( (query) => query @@ -972,8 +830,7 @@ void main() { final sB = StringBuffer(); sB.write("SELECT * FROM users WHERE name = 'Chima' "); - sB.write( - "OR (biscuit = 'hello-world' OR (car_type = 'lamborgini server' AND image LIKE 'Image&&')) "); + sB.write("OR (biscuit = 'hello-world' OR (car_type = 'lamborgini server' AND image LIKE 'Image&&')) "); sB.write( "AND (fruits IN ('oranges', 'apples') AND price BETWEEN 20 AND 100 AND status = 'available' AND stores LIKE 'Accra, %%');"); @@ -981,8 +838,7 @@ void main() { }); test('with level 3', () { - final query = Query.table('users') - .driver(driver) + final query = UserQuery.driver(driver) .where('name', '=', 'Chima') .orWhereFunc( (query) => query @@ -995,17 +851,14 @@ void main() { .isBetween('price', [20, 100]) .equal('status', 'available') .isLike('stores', 'Accra, %%')) - .orWhereFunc((query) => query - .where('languages', 'in', ['python', 'cobra']).orWhereFunc( - (query) => query - .where('job_status', '=', 'available') - .where('location', '=', 'Accra') - .isNotBetween('salary', [8000, 16000]))); + .orWhereFunc((query) => query.where('languages', 'in', ['python', 'cobra']).orWhereFunc((query) => query + .where('job_status', '=', 'available') + .where('location', '=', 'Accra') + .isNotBetween('salary', [8000, 16000]))); final sB = StringBuffer(); sB.write("SELECT * FROM users WHERE name = 'Chima' "); - sB.write( - "OR (biscuit = 'hello-world' OR (car_type = 'lamborgini server' AND image LIKE 'Image&&')) "); + sB.write("OR (biscuit = 'hello-world' OR (car_type = 'lamborgini server' AND image LIKE 'Image&&')) "); sB.write( "AND (fruits IN ('oranges', 'apples') AND price BETWEEN 20 AND 100 AND status = 'available' AND stores LIKE 'Accra, %%') "); sB.write( diff --git a/packages/yaroorm/test/yaroorm_test.dart b/packages/yaroorm/test/yaroorm_test.dart index d21bdaf9..d3199a6e 100644 --- a/packages/yaroorm/test/yaroorm_test.dart +++ b/packages/yaroorm/test/yaroorm_test.dart @@ -5,6 +5,7 @@ import 'package:yaroorm/src/database/driver/sqlite_driver.dart'; import 'package:yaroorm/yaroorm.dart'; import 'integration/fixtures/orm_config.dart' as db; +import 'models/models.dart'; Matcher throwsArgumentErrorWithMessage(String message) => throwsA(isA().having((p0) => p0.message, '', message)); @@ -19,10 +20,7 @@ void main() { setUpAll(() => driver = DB.driver('foo_sqlite')); test('should return SQLite Driver', () { - expect( - driver, - isA().having( - (p0) => p0.type, 'has driver type', DatabaseDriverType.sqlite)); + expect(driver, isA().having((p0) => p0.type, 'has driver type', DatabaseDriverType.sqlite)); }); test('should have table blueprint', () { @@ -40,10 +38,7 @@ void main() { setUpAll(() => driver = DB.driver('moo_mysql')); test('should return MySql Driver', () { - expect( - driver, - isA().having( - (p0) => p0.type, 'has driver type', DatabaseDriverType.mysql)); + expect(driver, isA().having((p0) => p0.type, 'has driver type', DatabaseDriverType.mysql)); }); test('should have table blueprint', () { @@ -61,10 +56,7 @@ void main() { setUpAll(() => driver = DB.driver('bar_mariadb')); test('should return MySql Driver', () { - expect( - driver, - isA().having((p0) => p0.type, 'has driver type', - DatabaseDriverType.mariadb)); + expect(driver, isA().having((p0) => p0.type, 'has driver type', DatabaseDriverType.mariadb)); }); test('should have table blueprint', () { @@ -82,10 +74,7 @@ void main() { setUpAll(() => driver = DB.driver('foo_pgsql')); test('should return Postgres Driver', () { - expect( - driver, - isA().having( - (p0) => p0.type, 'has driver type', DatabaseDriverType.pgsql)); + expect(driver, isA().having((p0) => p0.type, 'has driver type', DatabaseDriverType.pgsql)); }); test('should have table blueprint', () { @@ -101,15 +90,15 @@ void main() { test('should err when Query without driver', () async { late Object error; try { - await Query.table('users').all(); + await UserQuery.all(); } catch (e) { error = e; } expect( error, - isA().having((p0) => p0.message, '', - 'Driver not set for query. Make sure you supply a driver using .driver()'), + isA() + .having((p0) => p0.message, '', 'Driver not set for query. Make sure you supply a driver using .driver()'), ); }); } From 5836b1dcd1f7a10b7810ea8a338b2c6133ef3221 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 01:22:00 +0000 Subject: [PATCH 23/50] tiny fix --- packages/yaroorm/test/yaroorm_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/yaroorm/test/yaroorm_test.dart b/packages/yaroorm/test/yaroorm_test.dart index d3199a6e..875fa80b 100644 --- a/packages/yaroorm/test/yaroorm_test.dart +++ b/packages/yaroorm/test/yaroorm_test.dart @@ -5,7 +5,7 @@ import 'package:yaroorm/src/database/driver/sqlite_driver.dart'; import 'package:yaroorm/yaroorm.dart'; import 'integration/fixtures/orm_config.dart' as db; -import 'models/models.dart'; +import 'integration/fixtures/models.dart'; Matcher throwsArgumentErrorWithMessage(String message) => throwsA(isA().having((p0) => p0.message, '', message)); From 625da1d33d58c2a1e59520e8bad257c3a27cb8c8 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 01:24:31 +0000 Subject: [PATCH 24/50] update integration tests --- .../yaroorm/test/integration/e2e_basic.dart | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/packages/yaroorm/test/integration/e2e_basic.dart b/packages/yaroorm/test/integration/e2e_basic.dart index 2fc285a3..2ff80d7b 100644 --- a/packages/yaroorm/test/integration/e2e_basic.dart +++ b/packages/yaroorm/test/integration/e2e_basic.dart @@ -97,20 +97,17 @@ void runBasicE2ETest(String connectionName) { }); }); - // test('should update user', () async { - // final user = await userQuery.get(); - // expect(user!.id, 1); + test('should update user', () async { + final user = await userQuery.get(); + expect(user!.id, 1); - // await userQuery.update( - // where: (query) => query.equal('id', user.id), - // value: user.copyWith(firstname: 'Red Oil', age: 100), - // ); + await userQuery.whereId(user.id).update(firstname: value('Red Oil'), age: value(100)); - // final userFromDB = await userQuery.get(user.id); - // expect(user, isNotNull); - // expect(userFromDB?.firstname, 'Red Oil'); - // expect(userFromDB?.age, 100); - // }); + final userFromDB = await userQuery.whereId(user.id).findOne(); + expect(user, isNotNull); + expect(userFromDB?.firstname, 'Red Oil'); + expect(userFromDB?.age, 100); + }); // test('should update many users', () async { // final age50Users = userQuery.equal('age', 50); From 23326c3d4cf4cea09be493bb45772d2e7744f584 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 10:52:34 +0000 Subject: [PATCH 25/50] wip --- .github/workflows/test.yml | 2 +- melos.yaml | 3 +++ packages/{yaroorm => orm}/.gitignore | 0 packages/{yaroorm => orm}/CHANGELOG.md | 0 packages/{yaroorm => orm}/LICENSE | 0 packages/{yaroorm => orm}/README.md | 0 packages/{yaroorm => orm}/analysis_options.yaml | 0 packages/{yaroorm => orm}/e2e_test.sh | 0 packages/{yaroorm => orm}/lib/src/config.dart | 0 packages/{yaroorm => orm}/lib/src/database/database.dart | 0 packages/{yaroorm => orm}/lib/src/database/driver/driver.dart | 0 .../{yaroorm => orm}/lib/src/database/driver/mysql_driver.dart | 0 .../{yaroorm => orm}/lib/src/database/driver/pgsql_driver.dart | 0 .../lib/src/database/driver/sqlite_driver.dart | 0 .../{yaroorm => orm}/lib/src/database/entity/converter.dart | 0 packages/{yaroorm => orm}/lib/src/database/entity/entity.dart | 0 .../{yaroorm => orm}/lib/src/database/entity/relations.dart | 0 packages/{yaroorm => orm}/lib/src/migration.dart | 0 packages/{yaroorm => orm}/lib/src/primitives/_where_impl.dart | 0 packages/{yaroorm => orm}/lib/src/primitives/serializer.dart | 0 packages/{yaroorm => orm}/lib/src/primitives/where.dart | 0 packages/{yaroorm => orm}/lib/src/query/aggregates.dart | 0 packages/{yaroorm => orm}/lib/src/query/query.dart | 0 packages/{yaroorm => orm}/lib/src/query/query_impl.dart | 0 packages/{yaroorm => orm}/lib/src/reflection.dart | 0 packages/{yaroorm => orm}/lib/src/utils.dart | 0 packages/{yaroorm => orm}/lib/yaroorm.dart | 0 packages/{yaroorm => orm}/pubspec.yaml | 0 packages/{yaroorm => orm}/test/integration/e2e_basic.dart | 0 packages/{yaroorm => orm}/test/integration/e2e_relation.dart | 0 .../{yaroorm => orm}/test/integration/fixtures/migrations.dart | 0 .../{yaroorm => orm}/test/integration/fixtures/migrator.dart | 0 .../{yaroorm => orm}/test/integration/fixtures/models.dart | 0 .../{yaroorm => orm}/test/integration/fixtures/orm_config.dart | 0 .../{yaroorm => orm}/test/integration/fixtures/test_data.dart | 0 packages/{yaroorm => orm}/test/integration/mariadb.e2e.dart | 0 packages/{yaroorm => orm}/test/integration/mysql.e2e.dart | 0 packages/{yaroorm => orm}/test/integration/pgsql.e2e.dart | 0 packages/{yaroorm => orm}/test/integration/sqlite.e2e.dart | 0 packages/{yaroorm => orm}/test/unit/dialects/sqlite_test.dart | 0 packages/{yaroorm => orm}/test/yaroorm_test.dart | 0 packages/{yaroo_cli => orm_cli}/.gitignore | 0 packages/{yaroo_cli => orm_cli}/CHANGELOG.md | 0 packages/{yaroo_cli => orm_cli}/README.md | 0 packages/{yaroo_cli => orm_cli}/analysis_options.yaml | 0 packages/{yaroo_cli => orm_cli}/bin/yaroo.dart | 0 packages/{yaroo_cli => orm_cli}/lib/orm.dart | 0 packages/{yaroo_cli => orm_cli}/lib/orm/_misc.dart | 0 packages/{yaroo_cli => orm_cli}/lib/orm/commands/command.dart | 0 .../lib/orm/commands/migrate_command.dart | 0 .../lib/orm/commands/migrate_fresh_command.dart | 0 .../lib/orm/commands/migrate_reset_command.dart | 0 .../lib/orm/commands/migrate_rollback_command.dart | 0 packages/{yaroo_cli => orm_cli}/lib/src/command_runner.dart | 0 packages/{yaroo_cli => orm_cli}/lib/src/logger.dart | 0 packages/{yaroo_cli => orm_cli}/lib/src/migration.dart | 0 packages/{yaroo_cli => orm_cli}/lib/src/migration.g.dart | 0 packages/{yaroo_cli => orm_cli}/lib/src/utils.dart | 0 packages/{yaroo_cli => orm_cli}/lib/yaroo_cli.dart | 0 packages/{yaroo_cli => orm_cli}/pubspec.yaml | 0 packages/{yaroo_cli => orm_cli}/test/yaroo_cli_test.dart | 0 61 files changed, 4 insertions(+), 1 deletion(-) rename packages/{yaroorm => orm}/.gitignore (100%) rename packages/{yaroorm => orm}/CHANGELOG.md (100%) rename packages/{yaroorm => orm}/LICENSE (100%) rename packages/{yaroorm => orm}/README.md (100%) rename packages/{yaroorm => orm}/analysis_options.yaml (100%) rename packages/{yaroorm => orm}/e2e_test.sh (100%) rename packages/{yaroorm => orm}/lib/src/config.dart (100%) rename packages/{yaroorm => orm}/lib/src/database/database.dart (100%) rename packages/{yaroorm => orm}/lib/src/database/driver/driver.dart (100%) rename packages/{yaroorm => orm}/lib/src/database/driver/mysql_driver.dart (100%) rename packages/{yaroorm => orm}/lib/src/database/driver/pgsql_driver.dart (100%) rename packages/{yaroorm => orm}/lib/src/database/driver/sqlite_driver.dart (100%) rename packages/{yaroorm => orm}/lib/src/database/entity/converter.dart (100%) rename packages/{yaroorm => orm}/lib/src/database/entity/entity.dart (100%) rename packages/{yaroorm => orm}/lib/src/database/entity/relations.dart (100%) rename packages/{yaroorm => orm}/lib/src/migration.dart (100%) rename packages/{yaroorm => orm}/lib/src/primitives/_where_impl.dart (100%) rename packages/{yaroorm => orm}/lib/src/primitives/serializer.dart (100%) rename packages/{yaroorm => orm}/lib/src/primitives/where.dart (100%) rename packages/{yaroorm => orm}/lib/src/query/aggregates.dart (100%) rename packages/{yaroorm => orm}/lib/src/query/query.dart (100%) rename packages/{yaroorm => orm}/lib/src/query/query_impl.dart (100%) rename packages/{yaroorm => orm}/lib/src/reflection.dart (100%) rename packages/{yaroorm => orm}/lib/src/utils.dart (100%) rename packages/{yaroorm => orm}/lib/yaroorm.dart (100%) rename packages/{yaroorm => orm}/pubspec.yaml (100%) rename packages/{yaroorm => orm}/test/integration/e2e_basic.dart (100%) rename packages/{yaroorm => orm}/test/integration/e2e_relation.dart (100%) rename packages/{yaroorm => orm}/test/integration/fixtures/migrations.dart (100%) rename packages/{yaroorm => orm}/test/integration/fixtures/migrator.dart (100%) rename packages/{yaroorm => orm}/test/integration/fixtures/models.dart (100%) rename packages/{yaroorm => orm}/test/integration/fixtures/orm_config.dart (100%) rename packages/{yaroorm => orm}/test/integration/fixtures/test_data.dart (100%) rename packages/{yaroorm => orm}/test/integration/mariadb.e2e.dart (100%) rename packages/{yaroorm => orm}/test/integration/mysql.e2e.dart (100%) rename packages/{yaroorm => orm}/test/integration/pgsql.e2e.dart (100%) rename packages/{yaroorm => orm}/test/integration/sqlite.e2e.dart (100%) rename packages/{yaroorm => orm}/test/unit/dialects/sqlite_test.dart (100%) rename packages/{yaroorm => orm}/test/yaroorm_test.dart (100%) rename packages/{yaroo_cli => orm_cli}/.gitignore (100%) rename packages/{yaroo_cli => orm_cli}/CHANGELOG.md (100%) rename packages/{yaroo_cli => orm_cli}/README.md (100%) rename packages/{yaroo_cli => orm_cli}/analysis_options.yaml (100%) rename packages/{yaroo_cli => orm_cli}/bin/yaroo.dart (100%) rename packages/{yaroo_cli => orm_cli}/lib/orm.dart (100%) rename packages/{yaroo_cli => orm_cli}/lib/orm/_misc.dart (100%) rename packages/{yaroo_cli => orm_cli}/lib/orm/commands/command.dart (100%) rename packages/{yaroo_cli => orm_cli}/lib/orm/commands/migrate_command.dart (100%) rename packages/{yaroo_cli => orm_cli}/lib/orm/commands/migrate_fresh_command.dart (100%) rename packages/{yaroo_cli => orm_cli}/lib/orm/commands/migrate_reset_command.dart (100%) rename packages/{yaroo_cli => orm_cli}/lib/orm/commands/migrate_rollback_command.dart (100%) rename packages/{yaroo_cli => orm_cli}/lib/src/command_runner.dart (100%) rename packages/{yaroo_cli => orm_cli}/lib/src/logger.dart (100%) rename packages/{yaroo_cli => orm_cli}/lib/src/migration.dart (100%) rename packages/{yaroo_cli => orm_cli}/lib/src/migration.g.dart (100%) rename packages/{yaroo_cli => orm_cli}/lib/src/utils.dart (100%) rename packages/{yaroo_cli => orm_cli}/lib/yaroo_cli.dart (100%) rename packages/{yaroo_cli => orm_cli}/pubspec.yaml (100%) rename packages/{yaroo_cli => orm_cli}/test/yaroo_cli_test.dart (100%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c080cb53..202a4769 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,7 +34,7 @@ jobs: - name: Check linting run: | - melos exec -- "dart run build_runner build" + melos builder melos analyze test: diff --git a/melos.yaml b/melos.yaml index 69ef1093..acaad255 100644 --- a/melos.yaml +++ b/melos.yaml @@ -19,6 +19,9 @@ scripts: tests:ci: run: | melos exec -c 1 -- "dart test --coverage=coverage" --fail-fast + + builder: + run: melos exec --ignore="yaroorm_builder" -- "dart run build_runner build" coverage: run: | diff --git a/packages/yaroorm/.gitignore b/packages/orm/.gitignore similarity index 100% rename from packages/yaroorm/.gitignore rename to packages/orm/.gitignore diff --git a/packages/yaroorm/CHANGELOG.md b/packages/orm/CHANGELOG.md similarity index 100% rename from packages/yaroorm/CHANGELOG.md rename to packages/orm/CHANGELOG.md diff --git a/packages/yaroorm/LICENSE b/packages/orm/LICENSE similarity index 100% rename from packages/yaroorm/LICENSE rename to packages/orm/LICENSE diff --git a/packages/yaroorm/README.md b/packages/orm/README.md similarity index 100% rename from packages/yaroorm/README.md rename to packages/orm/README.md diff --git a/packages/yaroorm/analysis_options.yaml b/packages/orm/analysis_options.yaml similarity index 100% rename from packages/yaroorm/analysis_options.yaml rename to packages/orm/analysis_options.yaml diff --git a/packages/yaroorm/e2e_test.sh b/packages/orm/e2e_test.sh similarity index 100% rename from packages/yaroorm/e2e_test.sh rename to packages/orm/e2e_test.sh diff --git a/packages/yaroorm/lib/src/config.dart b/packages/orm/lib/src/config.dart similarity index 100% rename from packages/yaroorm/lib/src/config.dart rename to packages/orm/lib/src/config.dart diff --git a/packages/yaroorm/lib/src/database/database.dart b/packages/orm/lib/src/database/database.dart similarity index 100% rename from packages/yaroorm/lib/src/database/database.dart rename to packages/orm/lib/src/database/database.dart diff --git a/packages/yaroorm/lib/src/database/driver/driver.dart b/packages/orm/lib/src/database/driver/driver.dart similarity index 100% rename from packages/yaroorm/lib/src/database/driver/driver.dart rename to packages/orm/lib/src/database/driver/driver.dart diff --git a/packages/yaroorm/lib/src/database/driver/mysql_driver.dart b/packages/orm/lib/src/database/driver/mysql_driver.dart similarity index 100% rename from packages/yaroorm/lib/src/database/driver/mysql_driver.dart rename to packages/orm/lib/src/database/driver/mysql_driver.dart diff --git a/packages/yaroorm/lib/src/database/driver/pgsql_driver.dart b/packages/orm/lib/src/database/driver/pgsql_driver.dart similarity index 100% rename from packages/yaroorm/lib/src/database/driver/pgsql_driver.dart rename to packages/orm/lib/src/database/driver/pgsql_driver.dart diff --git a/packages/yaroorm/lib/src/database/driver/sqlite_driver.dart b/packages/orm/lib/src/database/driver/sqlite_driver.dart similarity index 100% rename from packages/yaroorm/lib/src/database/driver/sqlite_driver.dart rename to packages/orm/lib/src/database/driver/sqlite_driver.dart diff --git a/packages/yaroorm/lib/src/database/entity/converter.dart b/packages/orm/lib/src/database/entity/converter.dart similarity index 100% rename from packages/yaroorm/lib/src/database/entity/converter.dart rename to packages/orm/lib/src/database/entity/converter.dart diff --git a/packages/yaroorm/lib/src/database/entity/entity.dart b/packages/orm/lib/src/database/entity/entity.dart similarity index 100% rename from packages/yaroorm/lib/src/database/entity/entity.dart rename to packages/orm/lib/src/database/entity/entity.dart diff --git a/packages/yaroorm/lib/src/database/entity/relations.dart b/packages/orm/lib/src/database/entity/relations.dart similarity index 100% rename from packages/yaroorm/lib/src/database/entity/relations.dart rename to packages/orm/lib/src/database/entity/relations.dart diff --git a/packages/yaroorm/lib/src/migration.dart b/packages/orm/lib/src/migration.dart similarity index 100% rename from packages/yaroorm/lib/src/migration.dart rename to packages/orm/lib/src/migration.dart diff --git a/packages/yaroorm/lib/src/primitives/_where_impl.dart b/packages/orm/lib/src/primitives/_where_impl.dart similarity index 100% rename from packages/yaroorm/lib/src/primitives/_where_impl.dart rename to packages/orm/lib/src/primitives/_where_impl.dart diff --git a/packages/yaroorm/lib/src/primitives/serializer.dart b/packages/orm/lib/src/primitives/serializer.dart similarity index 100% rename from packages/yaroorm/lib/src/primitives/serializer.dart rename to packages/orm/lib/src/primitives/serializer.dart diff --git a/packages/yaroorm/lib/src/primitives/where.dart b/packages/orm/lib/src/primitives/where.dart similarity index 100% rename from packages/yaroorm/lib/src/primitives/where.dart rename to packages/orm/lib/src/primitives/where.dart diff --git a/packages/yaroorm/lib/src/query/aggregates.dart b/packages/orm/lib/src/query/aggregates.dart similarity index 100% rename from packages/yaroorm/lib/src/query/aggregates.dart rename to packages/orm/lib/src/query/aggregates.dart diff --git a/packages/yaroorm/lib/src/query/query.dart b/packages/orm/lib/src/query/query.dart similarity index 100% rename from packages/yaroorm/lib/src/query/query.dart rename to packages/orm/lib/src/query/query.dart diff --git a/packages/yaroorm/lib/src/query/query_impl.dart b/packages/orm/lib/src/query/query_impl.dart similarity index 100% rename from packages/yaroorm/lib/src/query/query_impl.dart rename to packages/orm/lib/src/query/query_impl.dart diff --git a/packages/yaroorm/lib/src/reflection.dart b/packages/orm/lib/src/reflection.dart similarity index 100% rename from packages/yaroorm/lib/src/reflection.dart rename to packages/orm/lib/src/reflection.dart diff --git a/packages/yaroorm/lib/src/utils.dart b/packages/orm/lib/src/utils.dart similarity index 100% rename from packages/yaroorm/lib/src/utils.dart rename to packages/orm/lib/src/utils.dart diff --git a/packages/yaroorm/lib/yaroorm.dart b/packages/orm/lib/yaroorm.dart similarity index 100% rename from packages/yaroorm/lib/yaroorm.dart rename to packages/orm/lib/yaroorm.dart diff --git a/packages/yaroorm/pubspec.yaml b/packages/orm/pubspec.yaml similarity index 100% rename from packages/yaroorm/pubspec.yaml rename to packages/orm/pubspec.yaml diff --git a/packages/yaroorm/test/integration/e2e_basic.dart b/packages/orm/test/integration/e2e_basic.dart similarity index 100% rename from packages/yaroorm/test/integration/e2e_basic.dart rename to packages/orm/test/integration/e2e_basic.dart diff --git a/packages/yaroorm/test/integration/e2e_relation.dart b/packages/orm/test/integration/e2e_relation.dart similarity index 100% rename from packages/yaroorm/test/integration/e2e_relation.dart rename to packages/orm/test/integration/e2e_relation.dart diff --git a/packages/yaroorm/test/integration/fixtures/migrations.dart b/packages/orm/test/integration/fixtures/migrations.dart similarity index 100% rename from packages/yaroorm/test/integration/fixtures/migrations.dart rename to packages/orm/test/integration/fixtures/migrations.dart diff --git a/packages/yaroorm/test/integration/fixtures/migrator.dart b/packages/orm/test/integration/fixtures/migrator.dart similarity index 100% rename from packages/yaroorm/test/integration/fixtures/migrator.dart rename to packages/orm/test/integration/fixtures/migrator.dart diff --git a/packages/yaroorm/test/integration/fixtures/models.dart b/packages/orm/test/integration/fixtures/models.dart similarity index 100% rename from packages/yaroorm/test/integration/fixtures/models.dart rename to packages/orm/test/integration/fixtures/models.dart diff --git a/packages/yaroorm/test/integration/fixtures/orm_config.dart b/packages/orm/test/integration/fixtures/orm_config.dart similarity index 100% rename from packages/yaroorm/test/integration/fixtures/orm_config.dart rename to packages/orm/test/integration/fixtures/orm_config.dart diff --git a/packages/yaroorm/test/integration/fixtures/test_data.dart b/packages/orm/test/integration/fixtures/test_data.dart similarity index 100% rename from packages/yaroorm/test/integration/fixtures/test_data.dart rename to packages/orm/test/integration/fixtures/test_data.dart diff --git a/packages/yaroorm/test/integration/mariadb.e2e.dart b/packages/orm/test/integration/mariadb.e2e.dart similarity index 100% rename from packages/yaroorm/test/integration/mariadb.e2e.dart rename to packages/orm/test/integration/mariadb.e2e.dart diff --git a/packages/yaroorm/test/integration/mysql.e2e.dart b/packages/orm/test/integration/mysql.e2e.dart similarity index 100% rename from packages/yaroorm/test/integration/mysql.e2e.dart rename to packages/orm/test/integration/mysql.e2e.dart diff --git a/packages/yaroorm/test/integration/pgsql.e2e.dart b/packages/orm/test/integration/pgsql.e2e.dart similarity index 100% rename from packages/yaroorm/test/integration/pgsql.e2e.dart rename to packages/orm/test/integration/pgsql.e2e.dart diff --git a/packages/yaroorm/test/integration/sqlite.e2e.dart b/packages/orm/test/integration/sqlite.e2e.dart similarity index 100% rename from packages/yaroorm/test/integration/sqlite.e2e.dart rename to packages/orm/test/integration/sqlite.e2e.dart diff --git a/packages/yaroorm/test/unit/dialects/sqlite_test.dart b/packages/orm/test/unit/dialects/sqlite_test.dart similarity index 100% rename from packages/yaroorm/test/unit/dialects/sqlite_test.dart rename to packages/orm/test/unit/dialects/sqlite_test.dart diff --git a/packages/yaroorm/test/yaroorm_test.dart b/packages/orm/test/yaroorm_test.dart similarity index 100% rename from packages/yaroorm/test/yaroorm_test.dart rename to packages/orm/test/yaroorm_test.dart diff --git a/packages/yaroo_cli/.gitignore b/packages/orm_cli/.gitignore similarity index 100% rename from packages/yaroo_cli/.gitignore rename to packages/orm_cli/.gitignore diff --git a/packages/yaroo_cli/CHANGELOG.md b/packages/orm_cli/CHANGELOG.md similarity index 100% rename from packages/yaroo_cli/CHANGELOG.md rename to packages/orm_cli/CHANGELOG.md diff --git a/packages/yaroo_cli/README.md b/packages/orm_cli/README.md similarity index 100% rename from packages/yaroo_cli/README.md rename to packages/orm_cli/README.md diff --git a/packages/yaroo_cli/analysis_options.yaml b/packages/orm_cli/analysis_options.yaml similarity index 100% rename from packages/yaroo_cli/analysis_options.yaml rename to packages/orm_cli/analysis_options.yaml diff --git a/packages/yaroo_cli/bin/yaroo.dart b/packages/orm_cli/bin/yaroo.dart similarity index 100% rename from packages/yaroo_cli/bin/yaroo.dart rename to packages/orm_cli/bin/yaroo.dart diff --git a/packages/yaroo_cli/lib/orm.dart b/packages/orm_cli/lib/orm.dart similarity index 100% rename from packages/yaroo_cli/lib/orm.dart rename to packages/orm_cli/lib/orm.dart diff --git a/packages/yaroo_cli/lib/orm/_misc.dart b/packages/orm_cli/lib/orm/_misc.dart similarity index 100% rename from packages/yaroo_cli/lib/orm/_misc.dart rename to packages/orm_cli/lib/orm/_misc.dart diff --git a/packages/yaroo_cli/lib/orm/commands/command.dart b/packages/orm_cli/lib/orm/commands/command.dart similarity index 100% rename from packages/yaroo_cli/lib/orm/commands/command.dart rename to packages/orm_cli/lib/orm/commands/command.dart diff --git a/packages/yaroo_cli/lib/orm/commands/migrate_command.dart b/packages/orm_cli/lib/orm/commands/migrate_command.dart similarity index 100% rename from packages/yaroo_cli/lib/orm/commands/migrate_command.dart rename to packages/orm_cli/lib/orm/commands/migrate_command.dart diff --git a/packages/yaroo_cli/lib/orm/commands/migrate_fresh_command.dart b/packages/orm_cli/lib/orm/commands/migrate_fresh_command.dart similarity index 100% rename from packages/yaroo_cli/lib/orm/commands/migrate_fresh_command.dart rename to packages/orm_cli/lib/orm/commands/migrate_fresh_command.dart diff --git a/packages/yaroo_cli/lib/orm/commands/migrate_reset_command.dart b/packages/orm_cli/lib/orm/commands/migrate_reset_command.dart similarity index 100% rename from packages/yaroo_cli/lib/orm/commands/migrate_reset_command.dart rename to packages/orm_cli/lib/orm/commands/migrate_reset_command.dart diff --git a/packages/yaroo_cli/lib/orm/commands/migrate_rollback_command.dart b/packages/orm_cli/lib/orm/commands/migrate_rollback_command.dart similarity index 100% rename from packages/yaroo_cli/lib/orm/commands/migrate_rollback_command.dart rename to packages/orm_cli/lib/orm/commands/migrate_rollback_command.dart diff --git a/packages/yaroo_cli/lib/src/command_runner.dart b/packages/orm_cli/lib/src/command_runner.dart similarity index 100% rename from packages/yaroo_cli/lib/src/command_runner.dart rename to packages/orm_cli/lib/src/command_runner.dart diff --git a/packages/yaroo_cli/lib/src/logger.dart b/packages/orm_cli/lib/src/logger.dart similarity index 100% rename from packages/yaroo_cli/lib/src/logger.dart rename to packages/orm_cli/lib/src/logger.dart diff --git a/packages/yaroo_cli/lib/src/migration.dart b/packages/orm_cli/lib/src/migration.dart similarity index 100% rename from packages/yaroo_cli/lib/src/migration.dart rename to packages/orm_cli/lib/src/migration.dart diff --git a/packages/yaroo_cli/lib/src/migration.g.dart b/packages/orm_cli/lib/src/migration.g.dart similarity index 100% rename from packages/yaroo_cli/lib/src/migration.g.dart rename to packages/orm_cli/lib/src/migration.g.dart diff --git a/packages/yaroo_cli/lib/src/utils.dart b/packages/orm_cli/lib/src/utils.dart similarity index 100% rename from packages/yaroo_cli/lib/src/utils.dart rename to packages/orm_cli/lib/src/utils.dart diff --git a/packages/yaroo_cli/lib/yaroo_cli.dart b/packages/orm_cli/lib/yaroo_cli.dart similarity index 100% rename from packages/yaroo_cli/lib/yaroo_cli.dart rename to packages/orm_cli/lib/yaroo_cli.dart diff --git a/packages/yaroo_cli/pubspec.yaml b/packages/orm_cli/pubspec.yaml similarity index 100% rename from packages/yaroo_cli/pubspec.yaml rename to packages/orm_cli/pubspec.yaml diff --git a/packages/yaroo_cli/test/yaroo_cli_test.dart b/packages/orm_cli/test/yaroo_cli_test.dart similarity index 100% rename from packages/yaroo_cli/test/yaroo_cli_test.dart rename to packages/orm_cli/test/yaroo_cli_test.dart From 8cbdcfe63ed7b9199bf59552c8af9fd4982ea694 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 10:55:28 +0000 Subject: [PATCH 26/50] tiny fix --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 202a4769..14cd0e79 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -78,7 +78,7 @@ jobs: dart pub global activate melos dart pub global activate coverage melos bootstrap - melos exec -- "dart run build_runner build" + melos builder - name: Run Unit Tests run: melos tests:ci From 06ccfb80eece26c25bdc038f8a582ab7e0b3a051 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 11:01:27 +0000 Subject: [PATCH 27/50] tiny fix --- packages/orm/lib/src/migration.dart | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/orm/lib/src/migration.dart b/packages/orm/lib/src/migration.dart index 332926bd..708fe804 100644 --- a/packages/orm/lib/src/migration.dart +++ b/packages/orm/lib/src/migration.dart @@ -2,6 +2,7 @@ library migration; import 'package:meta/meta.dart'; import 'package:recase/recase.dart'; +import 'package:yaroorm/yaroorm.dart'; import 'database/entity/entity.dart'; import 'query/query.dart'; @@ -169,12 +170,12 @@ abstract class Schema { static CreateSchema fromEntity() { final entity = Query.getEntity(); - void make(TableBlueprint table, DBEntityField field) { + TableBlueprint make(TableBlueprint table, DBEntityField field) { return switch (field.type) { - const (int) => table.integer(field.columnName, nullable: field.nullable), - const (double) || const (num) => table.double(field.columnName, nullable: field.nullable), - const (DateTime) => table.datetime(field.columnName, nullable: field.nullable), - _ => table.string(field.columnName, nullable: field.nullable), + const (int) => table..integer(field.columnName, nullable: field.nullable), + const (double) || const (num) => table..double(field.columnName, nullable: field.nullable), + const (DateTime) => table..datetime(field.columnName, nullable: field.nullable), + _ => table..string(field.columnName, nullable: field.nullable), }; } @@ -187,7 +188,7 @@ abstract class Schema { ); for (final prop in entity.columns.where((e) => !e.isPrimaryKey)) { - make(table, prop); + table = make(table, prop); } for (final prop in entity.referencedFields) { From cc2e1f57c1cb2496796071a6dadeced2bb36b876 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 11:02:52 +0000 Subject: [PATCH 28/50] tiny fix --- packages/orm/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/orm/pubspec.yaml b/packages/orm/pubspec.yaml index f5d9e248..64eb1e62 100644 --- a/packages/orm/pubspec.yaml +++ b/packages/orm/pubspec.yaml @@ -29,7 +29,7 @@ dev_dependencies: lints: ^3.0.0 test: ^1.24.0 yaroo_cli: - path: ../yaroo_cli + path: ../orm_cli path: ^1.9.0 collection: ^1.18.0 build_runner: From c31001ebe0529b8d5c0814eca1ab551b20b4113d Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 11:04:10 +0000 Subject: [PATCH 29/50] Tiny fix --- packages/orm/test/integration/fixtures/migrations.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/orm/test/integration/fixtures/migrations.dart b/packages/orm/test/integration/fixtures/migrations.dart index cdfe5b78..0a60cd86 100644 --- a/packages/orm/test/integration/fixtures/migrations.dart +++ b/packages/orm/test/integration/fixtures/migrations.dart @@ -17,9 +17,7 @@ class AddUsersTable extends Migration { class AddPostsTable extends Migration { @override void up(List schemas) { - final postSchema = PostSchema; - - schemas.addAll([postSchema, PostCommentSchema]); + schemas.addAll([PostSchema, PostCommentSchema]); } @override From 102b3362b712ce8d903bfe9b2d9af93e3a955fab Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 11:11:30 +0000 Subject: [PATCH 30/50] tiny fixes --- .github/workflows/test.yml | 102 ++++----- melos.yaml | 2 +- packages/orm/lib/src/database/database.dart | 9 +- .../orm/lib/src/database/driver/driver.dart | 3 +- .../lib/src/database/driver/mysql_driver.dart | 205 +++++------------- .../lib/src/database/driver/pgsql_driver.dart | 59 ++--- .../src/database/driver/sqlite_driver.dart | 179 ++++----------- .../orm/lib/src/primitives/_where_impl.dart | 15 +- packages/orm/lib/src/query/aggregates.dart | 8 +- packages/orm/lib/src/reflection.dart | 31 +-- .../test/integration/fixtures/migrator.dart | 7 +- .../test/integration/fixtures/test_data.dart | 203 +++-------------- 12 files changed, 230 insertions(+), 593 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 14cd0e79..483afb58 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,61 +37,61 @@ jobs: melos builder melos analyze - test: - name: Test Packages - runs-on: ubuntu-latest - services: - mariadb: - image: mariadb - env: - MYSQL_ROOT_PASSWORD: password - MARIADB_DATABASE: test_db - ports: - - 3000:3306 - mysql: - image: mysql - env: - MYSQL_ROOT_PASSWORD: password - MYSQL_DATABASE: test_db - ports: - - 3001:3306 - postgres: - image: postgres:latest - env: - POSTGRES_DB: test_db - POSTGRES_PASSWORD: password - POSTGRES_USER: root - ports: - - 3002:5432 + # test: + # name: Test Packages + # runs-on: ubuntu-latest + # services: + # mariadb: + # image: mariadb + # env: + # MYSQL_ROOT_PASSWORD: password + # MARIADB_DATABASE: test_db + # ports: + # - 3000:3306 + # mysql: + # image: mysql + # env: + # MYSQL_ROOT_PASSWORD: password + # MYSQL_DATABASE: test_db + # ports: + # - 3001:3306 + # postgres: + # image: postgres:latest + # env: + # POSTGRES_DB: test_db + # POSTGRES_PASSWORD: password + # POSTGRES_USER: root + # ports: + # - 3002:5432 - steps: - - name: Checkout Repository - uses: actions/checkout@v3 + # steps: + # - name: Checkout Repository + # uses: actions/checkout@v3 - - uses: dart-lang/setup-dart@v1.3 - with: - sdk: "3.0.0" - - uses: bluefireteam/melos-action@v3 + # - uses: dart-lang/setup-dart@v1.3 + # with: + # sdk: "3.0.0" + # - uses: bluefireteam/melos-action@v3 - - name: Prepare Workspace - run: | - dart pub global activate melos - dart pub global activate coverage - melos bootstrap - melos builder + # - name: Prepare Workspace + # run: | + # dart pub global activate melos + # dart pub global activate coverage + # melos bootstrap + # melos builder - - name: Run Unit Tests - run: melos tests:ci + # - name: Run Unit Tests + # run: melos tests:ci - - name: Run E2E Tests - run: cd packages/yaroorm && "./e2e_test.sh" + # - name: Run E2E Tests + # run: cd packages/yaroorm && "./e2e_test.sh" - - name: Combine Coverage - run: melos coverage + # - name: Combine Coverage + # run: melos coverage - - name: Upload Coverage - uses: codecov/codecov-action@v3 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - with: - files: coverage/*_lcov.info + # - name: Upload Coverage + # uses: codecov/codecov-action@v3 + # env: + # CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + # with: + # files: coverage/*_lcov.info diff --git a/melos.yaml b/melos.yaml index acaad255..fef196e9 100644 --- a/melos.yaml +++ b/melos.yaml @@ -28,6 +28,6 @@ scripts: melos exec -- "dart pub global run coverage:format_coverage --check-ignore --report-on=lib --lcov -o "$MELOS_ROOT_PATH/coverage/$(echo "\$MELOS_PACKAGE_NAME")_lcov.info" -i ./coverage" find $MELOS_ROOT_PATH/coverage -type f -empty -print -delete - format: melos exec -- "dart format ." + format: melos exec -- "dart format . --line-length=120" analyze: melos exec -- "dart analyze . --fatal-infos --no-fatal-warnings" diff --git a/packages/orm/lib/src/database/database.dart b/packages/orm/lib/src/database/database.dart index b8c16010..d9c2587f 100644 --- a/packages/orm/lib/src/database/database.dart +++ b/packages/orm/lib/src/database/database.dart @@ -28,12 +28,10 @@ class DB { static DatabaseDriver get defaultDriver => defaultConnection.driver; - static Query query([String? table]) => - defaultConnection.query(table); + static Query query([String? table]) => defaultConnection.query(table); static UseDatabaseConnection connection(String connName) => - UseDatabaseConnection( - config.connections.firstWhere((e) => e.name == connName)); + UseDatabaseConnection(config.connections.firstWhere((e) => e.name == connName)); /// This call returns the driver for a connection /// @@ -42,8 +40,7 @@ class DB { if (connName == 'default') return defaultDriver; final instance = _driverInstances[connName]; if (instance != null) return instance; - final connInfo = - config.connections.firstWhereOrNull((e) => e.name == connName); + final connInfo = config.connections.firstWhereOrNull((e) => e.name == connName); if (connInfo == null) { throw ArgumentError.value( connName, diff --git a/packages/orm/lib/src/database/driver/driver.dart b/packages/orm/lib/src/database/driver/driver.dart index ca2cf9b4..8df5f009 100644 --- a/packages/orm/lib/src/database/driver/driver.dart +++ b/packages/orm/lib/src/database/driver/driver.dart @@ -66,8 +66,7 @@ class DatabaseConnection { 'pgsql' => DatabaseDriverType.pgsql, 'mysql' => DatabaseDriverType.mysql, 'mariadb' => DatabaseDriverType.mariadb, - _ => throw ArgumentError.value( - driver, null, 'Invalid Database Driver provided in configuration') + _ => throw ArgumentError.value(driver, null, 'Invalid Database Driver provided in configuration') }; } } diff --git a/packages/orm/lib/src/database/driver/mysql_driver.dart b/packages/orm/lib/src/database/driver/mysql_driver.dart index 04d42e9a..aba00cf5 100644 --- a/packages/orm/lib/src/database/driver/mysql_driver.dart +++ b/packages/orm/lib/src/database/driver/mysql_driver.dart @@ -21,16 +21,13 @@ final class MySqlDriver implements DatabaseDriver { int get portToUse => config.port ?? 3306; MySqlDriver(this.config, this._type) { - assert([DatabaseDriverType.mysql, DatabaseDriverType.mariadb] - .contains(config.driver)); + assert([DatabaseDriverType.mysql, DatabaseDriverType.mariadb].contains(config.driver)); assert(config.host != null, 'Host is required'); } @override - Future connect( - {int? maxConnections, bool? singleConnection}) async { - assert(maxConnections == null, - '${_type.name} max connections not yet supported'); + Future connect({int? maxConnections, bool? singleConnection}) async { + assert(maxConnections == null, '${_type.name} max connections not yet supported'); final secure = config.secure ?? false; if (secure) { @@ -83,15 +80,13 @@ final class MySqlDriver implements DatabaseDriver { @override Future>> update(UpdateQuery query) async { - final result = await _dbConnection.execute( - _serializer.acceptUpdateQuery(query), query.data); + final result = await _dbConnection.execute(_serializer.acceptUpdateQuery(query), query.data); return result.rows.map((e) => e.typedAssoc()).toList(); } @override Future insert(InsertQuery query) async { - final result = await _dbConnection.execute( - _serializer.acceptInsertQuery(query), query.data); + final result = await _dbConnection.execute(_serializer.acceptInsertQuery(query), query.data); return result.lastInsertID.toInt(); } @@ -163,8 +158,7 @@ class _MysqlTransactor extends DriverTransactor { @override Future insert(InsertQuery query) async { - final result = - await _dbConn.execute(_serializer.acceptInsertQuery(query), query.data); + final result = await _dbConn.execute(_serializer.acceptInsertQuery(query), query.data); return result.lastInsertID.toInt(); } @@ -191,112 +185,91 @@ class MySqlDriverTableBlueprint extends SqliteTableBlueprint { void id({String name = 'id', String? type, bool autoIncrement = true}) { type ??= 'INT'; - final sb = StringBuffer() - ..write('${_serializer.escapeStr(name)} $type NOT NULL PRIMARY KEY'); + final sb = StringBuffer()..write('${_serializer.escapeStr(name)} $type NOT NULL PRIMARY KEY'); if (autoIncrement) sb.write(' AUTO_INCREMENT'); statements.add(sb.toString()); } @override - void string(String name, - {bool nullable = false, String? defaultValue, int length = 255}) { - statements.add(makeColumn(name, 'VARCHAR($length)', - nullable: nullable, defaultValue: defaultValue)); + void string(String name, {bool nullable = false, String? defaultValue, int length = 255}) { + statements.add(makeColumn(name, 'VARCHAR($length)', nullable: nullable, defaultValue: defaultValue)); } @override void datetime(String name, {bool nullable = false, DateTime? defaultValue}) { - statements.add(makeColumn(name, 'DATETIME', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'DATETIME', nullable: nullable, defaultValue: defaultValue)); } @override void timestamp(String name, {bool nullable = false, DateTime? defaultValue}) { - statements.add(makeColumn(name, 'TIMESTAMP', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'TIMESTAMP', nullable: nullable, defaultValue: defaultValue)); } @override void date(String name, {bool nullable = false, DateTime? defaultValue}) { - statements.add(makeColumn(name, 'DATE', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'DATE', nullable: nullable, defaultValue: defaultValue)); } @override void time(String name, {bool nullable = false, DateTime? defaultValue}) { - statements.add(makeColumn(name, 'TIME', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'TIME', nullable: nullable, defaultValue: defaultValue)); } @override void boolean(String name, {bool nullable = false, bool? defaultValue}) { - statements.add(makeColumn(name, 'BOOLEAN', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'BOOLEAN', nullable: nullable, defaultValue: defaultValue)); } /// NUMERIC TYPES /// ---------------------------------------------------------------- @override - void float(String name, - {bool nullable = false, num? defaultValue, int? precision, int? scale}) { + void float(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}) { final type = 'FLOAT(${precision ?? 10}, ${scale ?? 0})'; - statements.add( - makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); } @override - void double(String name, - {bool nullable = false, num? defaultValue, int? precision, int? scale}) { + void double(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}) { final type = 'DOUBLE(${precision ?? 10}, ${scale ?? 0})'; - statements.add( - makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); } @override void tinyInt(String name, {bool nullable = false, num? defaultValue}) { - statements.add(makeColumn(name, 'TINYINT', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'TINYINT', nullable: nullable, defaultValue: defaultValue)); } @override void smallInteger(String name, {bool nullable = false, num? defaultValue}) { - statements.add(makeColumn(name, 'SMALLINT', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'SMALLINT', nullable: nullable, defaultValue: defaultValue)); } @override void mediumInteger(String name, {bool nullable = false, num? defaultValue}) { - statements.add(makeColumn(name, 'MEDIUMINT', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'MEDIUMINT', nullable: nullable, defaultValue: defaultValue)); } @override void bigInteger(String name, {bool nullable = false, num? defaultValue}) { - statements.add(makeColumn(name, 'BIGINT', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'BIGINT', nullable: nullable, defaultValue: defaultValue)); } @override - void decimal(String name, - {bool nullable = false, num? defaultValue, int? precision, int? scale}) { + void decimal(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}) { final type = 'DECIMAL(${precision ?? 10}, ${scale ?? 0})'; - statements.add( - makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); } @override - void numeric(String name, - {bool nullable = false, num? defaultValue, int? precision, int? scale}) { + void numeric(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}) { final type = 'NUMERIC(${precision ?? 10}, ${scale ?? 0})'; - statements.add( - makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); } @override void bit(String name, {bool nullable = false, int? defaultValue}) { - statements.add(makeColumn(name, 'BIT', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'BIT', nullable: nullable, defaultValue: defaultValue)); } /// STRING TYPES @@ -313,132 +286,75 @@ class MySqlDriverTableBlueprint extends SqliteTableBlueprint { @override void blob(String name, {bool nullable = false, defaultValue}) { final type = _getStringType('BLOB'); - statements - .add(makeColumn(name, type, nullable: nullable, defaultValue: null)); + statements.add(makeColumn(name, type, nullable: nullable, defaultValue: null)); } /// TEXT type cannot have default values see here: https://dev.mysql.com/doc/refman/8.0/en/blob.html @override void text(String name, - {bool nullable = false, - String? defaultValue, - String? charset, - String? collate, - int length = 1}) { - final type = - _getStringType('TEXT($length)', charset: charset, collate: collate); - statements - .add(makeColumn(name, type, nullable: nullable, defaultValue: null)); - } - - @override - void longText(String name, - {bool nullable = false, - String? defaultValue, - String? charset, - String? collate}) { + {bool nullable = false, String? defaultValue, String? charset, String? collate, int length = 1}) { + final type = _getStringType('TEXT($length)', charset: charset, collate: collate); + statements.add(makeColumn(name, type, nullable: nullable, defaultValue: null)); + } + + @override + void longText(String name, {bool nullable = false, String? defaultValue, String? charset, String? collate}) { final type = _getStringType('LONGTEXT', charset: charset, collate: collate); - statements.add( - makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); } @override - void mediumText(String name, - {bool nullable = false, - String? defaultValue, - String? charset, - String? collate}) { - final type = - _getStringType('MEDIUMTEXT', charset: charset, collate: collate); - statements.add( - makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); + void mediumText(String name, {bool nullable = false, String? defaultValue, String? charset, String? collate}) { + final type = _getStringType('MEDIUMTEXT', charset: charset, collate: collate); + statements.add(makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); } @override - void tinyText(String name, - {bool nullable = false, - String? defaultValue, - String? charset, - String? collate}) { + void tinyText(String name, {bool nullable = false, String? defaultValue, String? charset, String? collate}) { final type = _getStringType('TINYTEXT', charset: charset, collate: collate); - statements.add( - makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); } @override void char(String name, - {bool nullable = false, - String? defaultValue, - String? charset, - String? collate, - int length = 1}) { - final type = - _getStringType('CHAR($length)', charset: charset, collate: collate); - statements.add( - makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); + {bool nullable = false, String? defaultValue, String? charset, String? collate, int length = 1}) { + final type = _getStringType('CHAR($length)', charset: charset, collate: collate); + statements.add(makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); } @override void varchar(String name, - {bool nullable = false, - String? defaultValue, - int length = 255, - String? charset, - String? collate}) { - final type = - _getStringType('VARCHAR($length)', charset: charset, collate: collate); - statements.add( - makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); + {bool nullable = false, String? defaultValue, int length = 255, String? charset, String? collate}) { + final type = _getStringType('VARCHAR($length)', charset: charset, collate: collate); + statements.add(makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); } @override void enums(String name, List values, - {bool nullable = false, - String? defaultValue, - String? charset, - String? collate}) { - final type = _getStringType('ENUM(${values.join(', ')})', - charset: charset, collate: collate); - statements.add( - makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); + {bool nullable = false, String? defaultValue, String? charset, String? collate}) { + final type = _getStringType('ENUM(${values.join(', ')})', charset: charset, collate: collate); + statements.add(makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); } @override void set(String name, List values, - {bool nullable = false, - String? defaultValue, - String? charset, - String? collate}) { - final type = _getStringType('SET(${values.join(', ')})', - charset: charset, collate: collate); - statements.add( - makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); + {bool nullable = false, String? defaultValue, String? charset, String? collate}) { + final type = _getStringType('SET(${values.join(', ')})', charset: charset, collate: collate); + statements.add(makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); } @override void binary(String name, - {bool nullable = false, - String? defaultValue, - String? charset, - String? collate, - int size = 1}) { - final type = - _getStringType('BINARY($size)', charset: charset, collate: collate); - statements.add( - makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); + {bool nullable = false, String? defaultValue, String? charset, String? collate, int size = 1}) { + final type = _getStringType('BINARY($size)', charset: charset, collate: collate); + statements.add(makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); } @override void varbinary(String name, - {bool nullable = false, - String? defaultValue, - String? charset, - String? collate, - int size = 1}) { - final type = - _getStringType('VARBINARY($size)', charset: charset, collate: collate); - statements.add( - makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); + {bool nullable = false, String? defaultValue, String? charset, String? collate, int size = 1}) { + final type = _getStringType('VARBINARY($size)', charset: charset, collate: collate); + statements.add(makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); } } @@ -457,8 +373,7 @@ class MySqlPrimitiveSerializer extends SqliteSerializer { String acceptUpdateQuery(UpdateQuery query) { final queryBuilder = StringBuffer(); - final fields = - query.data.keys.map((e) => '${escapeStr(e)} = :$e').join(', '); + final fields = query.data.keys.map((e) => '${escapeStr(e)} = :$e').join(', '); queryBuilder.write('UPDATE ${escapeStr(query.tableName)}'); diff --git a/packages/orm/lib/src/database/driver/pgsql_driver.dart b/packages/orm/lib/src/database/driver/pgsql_driver.dart index fbb3e51f..c628237f 100644 --- a/packages/orm/lib/src/database/driver/pgsql_driver.dart +++ b/packages/orm/lib/src/database/driver/pgsql_driver.dart @@ -17,8 +17,7 @@ final class PostgreSqlDriver implements DatabaseDriver { PostgreSqlDriver(this.config); @override - Future connect( - {int? maxConnections, bool? singleConnection, bool? secure}) async { + Future connect({int? maxConnections, bool? singleConnection, bool? secure}) async { assert(maxConnections == null, 'Postgres max connections not supported'); secure ??= false; @@ -55,12 +54,10 @@ final class PostgreSqlDriver implements DatabaseDriver { await db?.close(); } - Future>> _execRawQuery(String script, - {Map? parameters}) async { + Future>> _execRawQuery(String script, {Map? parameters}) async { parameters ??= {}; if (!isOpen) await connect(); - final result = - await db!.execute(pg.Sql.named(script), parameters: parameters); + final result = await db!.execute(pg.Sql.named(script), parameters: parameters); return result.map((e) => e.toColumnMap()).toList(); } @@ -72,8 +69,7 @@ final class PostgreSqlDriver implements DatabaseDriver { if (!isOpen) await connect(); final primaryKey = await _getPrimaryKeyColumn(query.tableName); final values = {...query.data}; - final sql = - _pgsqlSerializer.acceptInsertQuery(query, primaryKey: primaryKey); + final sql = _pgsqlSerializer.acceptInsertQuery(query, primaryKey: primaryKey); final result = await db!.execute(pg.Sql.named(sql), parameters: values); return result[0][0]; } @@ -89,8 +85,7 @@ final class PostgreSqlDriver implements DatabaseDriver { @override Future>> update(UpdateQuery query) { - return _execRawQuery(serializer.acceptUpdateQuery(query), - parameters: query.data); + return _execRawQuery(serializer.acceptUpdateQuery(query), parameters: query.data); } @override @@ -112,12 +107,10 @@ final class PostgreSqlDriver implements DatabaseDriver { } @override - Future>> rawQuery(String script) => - _execRawQuery(script); + Future>> rawQuery(String script) => _execRawQuery(script); @override - Future transaction( - void Function(DriverTransactor transactor) func) async { + Future transaction(void Function(DriverTransactor transactor) func) async { if (!isOpen) await connect(); if (db == null) return Future.value(); return db!.runTx((txn) async => func(_PgSqlDriverTransactor(txn))); @@ -210,8 +203,7 @@ class PgSqlPrimitiveSerializer extends MySqlPrimitiveSerializer { String acceptInsertQuery(InsertQuery query, {String? primaryKey}) { final keys = query.data.keys; final parameters = keys.map((e) => '@$e').join(', '); - final sql = - 'INSERT INTO ${query.tableName} (${keys.map(escapeStr).join(', ')}) VALUES ($parameters)'; + final sql = 'INSERT INTO ${query.tableName} (${keys.map(escapeStr).join(', ')}) VALUES ($parameters)'; if (primaryKey == null) return '$sql$terminator'; return '$sql RETURNING "$primaryKey"$terminator'; } @@ -220,8 +212,7 @@ class PgSqlPrimitiveSerializer extends MySqlPrimitiveSerializer { String acceptUpdateQuery(UpdateQuery query) { final queryBuilder = StringBuffer(); - final fields = - query.data.keys.map((e) => '${escapeStr(e)} = @$e').join(', '); + final fields = query.data.keys.map((e) => '${escapeStr(e)} = @$e').join(', '); queryBuilder.write('UPDATE ${escapeStr(query.tableName)}'); @@ -263,20 +254,17 @@ class PgSqlTableBlueprint extends MySqlDriverTableBlueprint { @override void datetime(String name, {bool nullable = false, DateTime? defaultValue}) { - statements.add(makeColumn(name, 'TIMESTAMP', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'TIMESTAMP', nullable: nullable, defaultValue: defaultValue)); } @override void blob(String name, {bool nullable = false, defaultValue}) { - statements - .add(makeColumn(name, "BYTEA", nullable: nullable, defaultValue: null)); + statements.add(makeColumn(name, "BYTEA", nullable: nullable, defaultValue: null)); } @override void boolean(String name, {nullable = false, defaultValue}) { - statements.add(makeColumn(name, 'BOOLEAN', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'BOOLEAN', nullable: nullable, defaultValue: defaultValue)); } @override @@ -292,8 +280,7 @@ class PgSqlTableBlueprint extends MySqlDriverTableBlueprint { int? precision, int? scale, }) { - statements.add(makeColumn(name, 'DOUBLE PRECISION', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'DOUBLE PRECISION', nullable: nullable, defaultValue: defaultValue)); } @override @@ -304,8 +291,7 @@ class PgSqlTableBlueprint extends MySqlDriverTableBlueprint { int? precision = 10, int? scale = 0, }) { - statements.add(makeColumn(name, 'NUMERIC($precision, $scale)', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'NUMERIC($precision, $scale)', nullable: nullable, defaultValue: defaultValue)); } @override @@ -323,8 +309,7 @@ class PgSqlTableBlueprint extends MySqlDriverTableBlueprint { bool nullable = false, num? defaultValue, }) { - statements.add(makeColumn(name, 'INTEGER', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'INTEGER', nullable: nullable, defaultValue: defaultValue)); } @override @@ -336,8 +321,7 @@ class PgSqlTableBlueprint extends MySqlDriverTableBlueprint { String? collate, int length = 1, }) { - statements - .add(makeColumn(name, 'TEXT', nullable: nullable, defaultValue: null)); + statements.add(makeColumn(name, 'TEXT', nullable: nullable, defaultValue: null)); } @override @@ -382,8 +366,7 @@ class PgSqlTableBlueprint extends MySqlDriverTableBlueprint { String? collate, int size = 1, }) { - statements.add(makeColumn(name, "BYTEA", - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, "BYTEA", nullable: nullable, defaultValue: defaultValue)); } @override @@ -396,8 +379,7 @@ class PgSqlTableBlueprint extends MySqlDriverTableBlueprint { int size = 1, }) { final type = 'BIT VARYING($size)'; - statements.add( - makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, type, nullable: nullable, defaultValue: defaultValue)); } @override @@ -422,10 +404,7 @@ class PgSqlTableBlueprint extends MySqlDriverTableBlueprint { @override void set(String name, List values, - {bool nullable = false, - String? defaultValue, - String? charset, - String? collate}) { + {bool nullable = false, String? defaultValue, String? charset, String? collate}) { throw UnimplementedError('set not implemented for Postgres'); } } diff --git a/packages/orm/lib/src/database/driver/sqlite_driver.dart b/packages/orm/lib/src/database/driver/sqlite_driver.dart index 9fa478f3..731546f1 100644 --- a/packages/orm/lib/src/database/driver/sqlite_driver.dart +++ b/packages/orm/lib/src/database/driver/sqlite_driver.dart @@ -13,10 +13,7 @@ import 'driver.dart'; final _serializer = const SqliteSerializer(); -const _sqliteTypeConverters = [ - booleanConverter, - dateTimeConverter -]; +const _sqliteTypeConverters = [booleanConverter, dateTimeConverter]; final class SqliteDriver implements DatabaseDriver { final DatabaseConnection config; @@ -31,13 +28,11 @@ final class SqliteDriver implements DatabaseDriver { bool? singleConnection, }) async { assert(maxConnections == null, 'Sqlite does not support max connections'); - assert( - singleConnection == null, 'Sqlite does not support single connection'); + assert(singleConnection == null, 'Sqlite does not support single connection'); sqfliteFfiInit(); var databaseFactory = databaseFactoryFfi; - _database = await databaseFactory.openDatabase(config.database, - options: OpenDatabaseOptions(onOpen: (db) async { + _database = await databaseFactory.openDatabase(config.database, options: OpenDatabaseOptions(onOpen: (db) async { if (config.dbForeignKeys) { await db.execute('PRAGMA foreign_keys = ON;'); } @@ -97,8 +92,7 @@ final class SqliteDriver implements DatabaseDriver { final batch = db.batch(); for (final entry in query.values) { - final sql = _serializer - .acceptInsertQuery(InsertQuery(query.tableName, data: entry)); + final sql = _serializer.acceptInsertQuery(InsertQuery(query.tableName, data: entry)); batch.rawInsert(sql, entry.values.toList()); } @@ -129,8 +123,7 @@ final class SqliteDriver implements DatabaseDriver { @override Future transaction(Function(DriverTransactor transactor) func) async { - return (await _getDatabase()) - .transaction((txn) => func(_SqliteTransactor(txn))); + return (await _getDatabase()).transaction((txn) => func(_SqliteTransactor(txn))); } } @@ -143,8 +136,7 @@ class _SqliteTransactor implements DriverTransactor { Future execute(String script) => _txn.execute(script); @override - Future>> rawQuery(String script) => - _txn.rawQuery(script); + Future>> rawQuery(String script) => _txn.rawQuery(script); @override Future>> query(Query query) async { @@ -175,8 +167,7 @@ class _SqliteTransactor implements DriverTransactor { final batch = _txn.batch(); for (final entry in query.values) { - final sql = _serializer - .acceptInsertQuery(InsertQuery(query.tableName, data: entry)); + final sql = _serializer.acceptInsertQuery(InsertQuery(query.tableName, data: entry)); batch.rawInsert(sql, entry.values.toList()); } @@ -201,8 +192,7 @@ class SqliteSerializer extends PrimitiveSerializer { final queryBuilder = StringBuffer(); final selection = '${aggregate.name}(${aggregate.arguments.join(', ')})'; - queryBuilder - .write('SELECT $selection FROM ${escapeStr(aggregate.tableName)}'); + queryBuilder.write('SELECT $selection FROM ${escapeStr(aggregate.tableName)}'); /// WHERE final clauses = aggregate.whereClauses; @@ -246,11 +236,7 @@ class SqliteSerializer extends PrimitiveSerializer { String _serializeWhereClauses(List clauses) { final sb = StringBuffer(); - final hasDifferentOperators = clauses - .map((e) => e.operators) - .reduce((val, e) => val..addAll(e)) - .length > - 1; + final hasDifferentOperators = clauses.map((e) => e.operators).reduce((val, e) => val..addAll(e)).length > 1; for (final clause in clauses) { final result = acceptWhereClause(clause, canGroup: hasDifferentOperators); @@ -305,9 +291,7 @@ class SqliteSerializer extends PrimitiveSerializer { @override String acceptSelect(List fields) { - return fields.isEmpty - ? 'SELECT * ' - : 'SELECT ${fields.map(escapeStr).join(', ')}'; + return fields.isEmpty ? 'SELECT * ' : 'SELECT ${fields.map(escapeStr).join(', ')}'; } @override @@ -318,8 +302,7 @@ class SqliteSerializer extends PrimitiveSerializer { final whereBb = StringBuffer(); - final List<(LogicalOperator operator, WhereClause clause)> groupMembers = - clause.group; + final List<(LogicalOperator operator, WhereClause clause)> groupMembers = clause.group; final shouldAddParenthesis = canGroup && groupMembers.length > 1; @@ -337,9 +320,7 @@ class SqliteSerializer extends PrimitiveSerializer { final childSerialized = acceptWhereClause(childValue); /// wrap sub clauses in parenthesis - shouldWrapChild - ? whereBb.write('($childSerialized)') - : whereBb.write(childSerialized); + shouldWrapChild ? whereBb.write('($childSerialized)') : whereBb.write(childSerialized); if (!isLast) whereBb.write(' '); } @@ -350,11 +331,8 @@ class SqliteSerializer extends PrimitiveSerializer { @override String acceptOrderBy(List orderBys) { - direction(OrderByDirection dir) => - dir == OrderByDirection.asc ? 'ASC' : 'DESC'; - return orderBys - .map((e) => '${e.field} ${direction(e.direction)}') - .join(', '); + direction(OrderByDirection dir) => dir == OrderByDirection.asc ? 'ASC' : 'DESC'; + return orderBys.map((e) => '${e.field} ${direction(e.direction)}').join(', '); } @override @@ -367,10 +345,7 @@ class SqliteSerializer extends PrimitiveSerializer { dynamic acceptPrimitiveValue(value) => switch (value.runtimeType) { const (int) || const (double) => value, const (List) => '(${value.map((e) => "'$e'").join(', ')})', - const (List) || - const (List) || - const (List) => - '(${value.join(', ')})', + const (List) || const (List) || const (List) => '(${value.join(', ')})', _ => "'$value'" }; @@ -399,8 +374,7 @@ class SqliteSerializer extends PrimitiveSerializer { Operator.NULL => '$field IS NULL', Operator.NOT_NULL => '$field IS NOT NULL', // - Operator.BETWEEN => - '$field BETWEEN ${acceptPrimitiveValue(value[0])} AND ${acceptPrimitiveValue(value[1])}', + Operator.BETWEEN => '$field BETWEEN ${acceptPrimitiveValue(value[0])} AND ${acceptPrimitiveValue(value[1])}', Operator.NOT_BETWEEN => '$field NOT BETWEEN ${acceptPrimitiveValue(value[0])} AND ${acceptPrimitiveValue(value[1])}', }; @@ -448,8 +422,7 @@ class SqliteTableBlueprint extends TableBlueprint { PrimitiveSerializer get szler => _serializer; - String makeColumn(String name, String type, - {nullable = false, defaultValue}) { + String makeColumn(String name, String type, {nullable = false, defaultValue}) { final sb = StringBuffer()..write('${szler.escapeStr(name)} $type'); if (!nullable) { sb.write(' NOT NULL'); @@ -465,54 +438,44 @@ class SqliteTableBlueprint extends TableBlueprint { void id({name = 'id', String? type, autoIncrement = true}) { type ??= 'INTEGER'; - final sb = StringBuffer() - ..write('${szler.escapeStr(name)} $type NOT NULL PRIMARY KEY'); + final sb = StringBuffer()..write('${szler.escapeStr(name)} $type NOT NULL PRIMARY KEY'); if (autoIncrement) sb.write(' AUTOINCREMENT'); statements.add(sb.toString()); } @override void string(String name, {nullable = false, defaultValue}) { - statements.add(makeColumn(name, 'VARCHAR', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'VARCHAR', nullable: nullable, defaultValue: defaultValue)); } @override - void double(String name, - {nullable = false, defaultValue, int? precision, int? scale}) { - statements.add(makeColumn(name, 'REAL', - nullable: nullable, defaultValue: defaultValue)); + void double(String name, {nullable = false, defaultValue, int? precision, int? scale}) { + statements.add(makeColumn(name, 'REAL', nullable: nullable, defaultValue: defaultValue)); } @override - void float(String name, - {nullable = false, defaultValue, int? precision, int? scale}) { - statements.add(makeColumn(name, 'REAL', - nullable: nullable, defaultValue: defaultValue)); + void float(String name, {nullable = false, defaultValue, int? precision, int? scale}) { + statements.add(makeColumn(name, 'REAL', nullable: nullable, defaultValue: defaultValue)); } @override void integer(String name, {nullable = false, defaultValue}) { - statements.add(makeColumn(name, 'INTEGER', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'INTEGER', nullable: nullable, defaultValue: defaultValue)); } @override void blob(String name, {nullable = false, defaultValue}) { - statements.add(makeColumn(name, 'BLOB', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'BLOB', nullable: nullable, defaultValue: defaultValue)); } @override void boolean(String name, {nullable = false, defaultValue}) { - statements.add(makeColumn(name, 'INTEGER', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'INTEGER', nullable: nullable, defaultValue: defaultValue)); } @override void datetime(String name, {nullable = false, defaultValue}) { - statements.add(makeColumn(name, 'DATETIME', - nullable: nullable, defaultValue: defaultValue?.toIso8601String())); + statements.add(makeColumn(name, 'DATETIME', nullable: nullable, defaultValue: defaultValue?.toIso8601String())); } @override @@ -541,71 +504,45 @@ class SqliteTableBlueprint extends TableBlueprint { @override void bigInteger(String name, {bool nullable = false, num? defaultValue}) { - statements.add(makeColumn(name, 'BIGINT', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'BIGINT', nullable: nullable, defaultValue: defaultValue)); } @override void binary(String name, - {bool nullable = false, - int size = 1, - String? defaultValue, - String? charset, - String? collate}) { - statements.add(makeColumn(name, 'BLOB', - nullable: nullable, defaultValue: defaultValue)); + {bool nullable = false, int size = 1, String? defaultValue, String? charset, String? collate}) { + statements.add(makeColumn(name, 'BLOB', nullable: nullable, defaultValue: defaultValue)); } @override void bit(String name, {bool nullable = false, int? defaultValue}) { - statements.add(makeColumn(name, 'INTEGER', - nullable: nullable, defaultValue: defaultValue)); + statements.add(makeColumn(name, 'INTEGER', nullable: nullable, defaultValue: defaultValue)); } @override void char(String name, - {bool nullable = false, - int length = 1, - String? defaultValue, - String? charset, - String? collate}) { - statements.add(makeColumn(name, 'CHAR($length)', - nullable: nullable, defaultValue: defaultValue)); + {bool nullable = false, int length = 1, String? defaultValue, String? charset, String? collate}) { + statements.add(makeColumn(name, 'CHAR($length)', nullable: nullable, defaultValue: defaultValue)); } @override - void decimal(String name, - {bool nullable = false, num? defaultValue, int? precision, int? scale}) { - statements.add(makeColumn(name, 'DECIMAL($precision, $scale)', - nullable: nullable, defaultValue: defaultValue)); + void decimal(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}) { + statements.add(makeColumn(name, 'DECIMAL($precision, $scale)', nullable: nullable, defaultValue: defaultValue)); } @override void enums(String name, List values, - {bool nullable = false, - String? defaultValue, - String? charset, - String? collate}) { - statements.add(makeColumn( - name, 'TEXT CHECK ($name IN (${values.map((e) => "'$e'").join(', ')}))', + {bool nullable = false, String? defaultValue, String? charset, String? collate}) { + statements.add(makeColumn(name, 'TEXT CHECK ($name IN (${values.map((e) => "'$e'").join(', ')}))', nullable: nullable, defaultValue: defaultValue)); } @override - void mediumText(String name, - {bool nullable = false, - String? defaultValue, - String? charset, - String? collate}) { + void mediumText(String name, {bool nullable = false, String? defaultValue, String? charset, String? collate}) { string(name, nullable: nullable, defaultValue: defaultValue); } @override - void longText(String name, - {bool nullable = false, - String? defaultValue, - String? charset, - String? collate}) { + void longText(String name, {bool nullable = false, String? defaultValue, String? charset, String? collate}) { string(name, nullable: nullable, defaultValue: defaultValue); } @@ -615,17 +552,13 @@ class SqliteTableBlueprint extends TableBlueprint { } @override - void numeric(String name, - {bool nullable = false, num? defaultValue, int? precision, int? scale}) { + void numeric(String name, {bool nullable = false, num? defaultValue, int? precision, int? scale}) { integer(name, nullable: nullable, defaultValue: defaultValue); } @override void set(String name, List values, - {bool nullable = false, - String? defaultValue, - String? charset, - String? collate}) { + {bool nullable = false, String? defaultValue, String? charset, String? collate}) { throw UnimplementedError(); } @@ -636,11 +569,7 @@ class SqliteTableBlueprint extends TableBlueprint { @override void text(String name, - {int length = 1, - bool nullable = false, - String? defaultValue, - String? charset, - String? collate}) { + {int length = 1, bool nullable = false, String? defaultValue, String? charset, String? collate}) { string(name, nullable: nullable, defaultValue: defaultValue); } @@ -650,31 +579,19 @@ class SqliteTableBlueprint extends TableBlueprint { } @override - void tinyText(String name, - {bool nullable = false, - String? defaultValue, - String? charset, - String? collate}) { + void tinyText(String name, {bool nullable = false, String? defaultValue, String? charset, String? collate}) { string(name, nullable: nullable, defaultValue: defaultValue); } @override void varbinary(String name, - {bool nullable = false, - int size = 1, - String? defaultValue, - String? charset, - String? collate}) { + {bool nullable = false, int size = 1, String? defaultValue, String? charset, String? collate}) { binary(name, nullable: nullable, defaultValue: defaultValue); } @override void varchar(String name, - {bool nullable = false, - String? defaultValue, - int length = 255, - String? charset, - String? collate}) { + {bool nullable = false, String? defaultValue, int length = 255, String? charset, String? collate}) { string(name, nullable: nullable, defaultValue: defaultValue); } @@ -693,8 +610,7 @@ class SqliteTableBlueprint extends TableBlueprint { String renameScript(String fromName, String toName) { final StringBuffer renameScript = StringBuffer(); renameScript - ..writeln( - 'CREATE TABLE temp_info AS SELECT * FROM PRAGMA table_info(\'$fromName\');') + ..writeln('CREATE TABLE temp_info AS SELECT * FROM PRAGMA table_info(\'$fromName\');') ..writeln('CREATE TABLE temp_data AS SELECT * FROM $fromName;') ..writeln('CREATE TABLE $toName AS SELECT * FROM temp_data WHERE 1 = 0;') ..writeln('INSERT INTO $toName SELECT * FROM temp_data;') @@ -704,8 +620,7 @@ class SqliteTableBlueprint extends TableBlueprint { @override String ensurePresenceOf(String column) { - final exactLine = - statements.firstWhereOrNull((e) => e.startsWith('$column ')); + final exactLine = statements.firstWhereOrNull((e) => e.startsWith('$column ')); if (exactLine == null) { throw Exception('Column $column not found in table blueprint'); } diff --git a/packages/orm/lib/src/primitives/_where_impl.dart b/packages/orm/lib/src/primitives/_where_impl.dart index af3ca042..7cc52069 100644 --- a/packages/orm/lib/src/primitives/_where_impl.dart +++ b/packages/orm/lib/src/primitives/_where_impl.dart @@ -83,8 +83,7 @@ class _WhereClauseImpl extends WhereClause { @override WhereClause isNotLike(String field, String pattern) { final newChild = _WhereClauseImpl(query) - ..clauseValue = WhereClauseValue( - field, (operator: Operator.NOT_LIKE, value: pattern)); + ..clauseValue = WhereClauseValue(field, (operator: Operator.NOT_LIKE, value: pattern)); children.add((LogicalOperator.AND, newChild)); return this; } @@ -113,9 +112,7 @@ class _WhereClauseImpl extends WhereClause { @override Future delete() { - return DeleteQuery(query.tableName, whereClause: this) - .driver(query.runner) - .execute(); + return DeleteQuery(query.tableName, whereClause: this).driver(query.runner).execute(); } @override @@ -145,8 +142,7 @@ class _WhereClauseImpl extends WhereClause { String condition, [ Value? value, ]) { - final newChild = _WhereClauseImpl(query) - ..clauseValue = WhereClauseValue.from(field, condition, value); + final newChild = _WhereClauseImpl(query)..clauseValue = WhereClauseValue.from(field, condition, value); children.add((LogicalOperator.AND, newChild)); return this; } @@ -157,9 +153,8 @@ class _WhereClauseImpl extends WhereClause { String condition, [ Value? value, ]) { - final newChild = - _WhereClauseImpl(query, operator: LogicalOperator.OR) - ..clauseValue = WhereClauseValue.from(field, condition, value); + final newChild = _WhereClauseImpl(query, operator: LogicalOperator.OR) + ..clauseValue = WhereClauseValue.from(field, condition, value); query.whereClauses.add(newChild); return newChild; } diff --git a/packages/orm/lib/src/query/aggregates.dart b/packages/orm/lib/src/query/aggregates.dart index cbade164..4f84eac8 100644 --- a/packages/orm/lib/src/query/aggregates.dart +++ b/packages/orm/lib/src/query/aggregates.dart @@ -38,10 +38,7 @@ sealed class AggregateFunction { final value = result[0].values.first; if (value is T) return value; return switch (T) { - const (int) || - const (double) || - const (num) => - value == null ? 0 : num.parse(value.toString()), + const (int) || const (double) || const (num) => value == null ? 0 : num.parse(value.toString()), const (String) => value.toString(), _ => throw Exception('Null value returned for aggregate: $statement'), } as T; @@ -88,8 +85,7 @@ class MinAggregate extends AggregateFunction { class GroupConcatAggregate extends AggregateFunction { final String separator; - GroupConcatAggregate(super.query, String super.field, this.separator) - : super._init(); + GroupConcatAggregate(super.query, String super.field, this.separator) : super._init(); @override List get arguments { diff --git a/packages/orm/lib/src/reflection.dart b/packages/orm/lib/src/reflection.dart index 10fbe44f..18528fa2 100644 --- a/packages/orm/lib/src/reflection.dart +++ b/packages/orm/lib/src/reflection.dart @@ -6,8 +6,7 @@ import 'query/query.dart'; String getEntityTableName() => Query.getEntity().tableName; -String getEntityPrimaryKey() => - Query.getEntity().primaryKey.columnName; +String getEntityPrimaryKey() => Query.getEntity().primaryKey.columnName; typedef EntityInstanceReflector = EntityMirror Function(T instance); @@ -33,22 +32,17 @@ final class DBEntity { final bool timestampsEnabled; - PrimaryKeyField get primaryKey => - columns.firstWhereOrNull((e) => e is PrimaryKeyField) as PrimaryKeyField; + PrimaryKeyField get primaryKey => columns.firstWhereOrNull((e) => e is PrimaryKeyField) as PrimaryKeyField; - CreatedAtField? get createdAtField => !timestampsEnabled - ? null - : columns.firstWhereOrNull((e) => e is CreatedAtField) as CreatedAtField?; + CreatedAtField? get createdAtField => + !timestampsEnabled ? null : columns.firstWhereOrNull((e) => e is CreatedAtField) as CreatedAtField?; - UpdatedAtField? get updatedAtField => !timestampsEnabled - ? null - : columns.firstWhereOrNull((e) => e is UpdatedAtField) as UpdatedAtField?; + UpdatedAtField? get updatedAtField => + !timestampsEnabled ? null : columns.firstWhereOrNull((e) => e is UpdatedAtField) as UpdatedAtField?; - Iterable get referencedFields => - columns.whereType(); + Iterable get referencedFields => columns.whereType(); - Iterable get editableColumns => - columns.where((e) => e != primaryKey); + Iterable get editableColumns => columns.where((e) => e != primaryKey); const DBEntity( this.tableName, { @@ -110,8 +104,7 @@ final class DBEntityField { ForeignKeyAction? onUpdate, ForeignKeyAction? onDelete, }) { - return ReferencedField._(columnName, dartName, - nullable: nullable, onUpdate: onUpdate, onDelete: onDelete); + return ReferencedField._(columnName, dartName, nullable: nullable, onUpdate: onUpdate, onDelete: onDelete); } } @@ -130,13 +123,11 @@ final class PrimaryKeyField extends DBEntityField { } final class CreatedAtField extends DBEntityField { - const CreatedAtField._(String columnName, Symbol dartName) - : super(columnName, DateTime, dartName); + const CreatedAtField._(String columnName, Symbol dartName) : super(columnName, DateTime, dartName); } final class UpdatedAtField extends DBEntityField { - const UpdatedAtField._(String columnName, Symbol dartName) - : super(columnName, DateTime, dartName); + const UpdatedAtField._(String columnName, Symbol dartName) : super(columnName, DateTime, dartName); } final class ReferencedField implements DBEntityField { diff --git a/packages/orm/test/integration/fixtures/migrator.dart b/packages/orm/test/integration/fixtures/migrator.dart index aef9a39a..3a6b0e5c 100644 --- a/packages/orm/test/integration/fixtures/migrator.dart +++ b/packages/orm/test/integration/fixtures/migrator.dart @@ -9,12 +9,7 @@ void main(List args) async { } Future runMigrator(String connectionName, String command) async { - final commands = [ - 'run', - 'test/integration/fixtures/migrator.dart', - command, - '--connection=$connectionName' - ]; + final commands = ['run', 'test/integration/fixtures/migrator.dart', command, '--connection=$connectionName']; print('> dart ${commands.join(' ')}\n'); final result = await Process.run('dart', commands); diff --git a/packages/orm/test/integration/fixtures/test_data.dart b/packages/orm/test/integration/fixtures/test_data.dart index 0c8342f1..a8852f76 100644 --- a/packages/orm/test/integration/fixtures/test_data.dart +++ b/packages/orm/test/integration/fixtures/test_data.dart @@ -1,191 +1,46 @@ -typedef UserData = ({ - String firstname, - String lastname, - int age, - String homeAddress -}); +typedef UserData = ({String firstname, String lastname, int age, String homeAddress}); final usersList = [ /// Ghana Users - [6] (firstname: 'Kofi', lastname: 'Duke', age: 22, homeAddress: "Accra, Ghana"), (firstname: 'Foo', lastname: 'Bar', age: 23, homeAddress: "Kumasi, Ghana"), - ( - firstname: 'Bar', - lastname: 'Moo', - age: 24, - homeAddress: "Cape Coast, Ghana" - ), + (firstname: 'Bar', lastname: 'Moo', age: 24, homeAddress: "Cape Coast, Ghana"), (firstname: 'Kee', lastname: 'Koo', age: 25, homeAddress: "Accra, Ghana"), (firstname: 'Poo', lastname: 'Paa', age: 26, homeAddress: "Accra, Ghana"), (firstname: 'Merh', lastname: 'Moor', age: 27, homeAddress: "Accra, Ghana"), /// Nigerian Users - [22] - ( - firstname: 'Abdul', - lastname: 'Ibrahim', - age: 28, - homeAddress: "Owerri, Nigeria" - ), - ( - firstname: 'Amina', - lastname: 'Sule', - age: 29, - homeAddress: "Owerri, Nigeria" - ), - ( - firstname: 'Chukwudi', - lastname: 'Okafor', - age: 30, - homeAddress: "Lagos, Nigeria" - ), - ( - firstname: 'Chioma', - lastname: 'Nwosu', - age: 31, - homeAddress: "Lagos, Nigeria" - ), - ( - firstname: 'Yusuf', - lastname: 'Aliyu', - age: 32, - homeAddress: "Owerri, Nigeria" - ), - ( - firstname: 'Blessing', - lastname: 'Okonkwo', - age: 33, - homeAddress: "Owerri, Nigeria" - ), - ( - firstname: 'Tunde', - lastname: 'Williams', - age: 34, - homeAddress: "Abuja, Nigeria" - ), - ( - firstname: 'Rukayat', - lastname: 'Sanni', - age: 35, - homeAddress: "Abuja, Nigeria" - ), - ( - firstname: 'Segun', - lastname: 'Adeleke', - age: 36, - homeAddress: "Lagos, Nigeria" - ), - ( - firstname: 'Abdullahi', - lastname: 'Mohammed', - age: 46, - homeAddress: "Lagos, Nigeria" - ), - ( - firstname: 'Chidinma', - lastname: 'Onyeka', - age: 47, - homeAddress: "Owerri, Nigeria" - ), - ( - firstname: 'Bola', - lastname: 'Akinwumi', - age: 48, - homeAddress: "Owerri, Nigeria" - ), - ( - firstname: 'Haruna', - lastname: 'Bello', - age: 49, - homeAddress: "Lagos, Nigeria" - ), - ( - firstname: 'Habiba', - lastname: 'Yusuf', - age: 50, - homeAddress: "Owerri, Nigeria" - ), - ( - firstname: 'Tochukwu', - lastname: 'Eze', - age: 50, - homeAddress: "Lagos, Nigeria" - ), - ( - firstname: 'Ade', - lastname: 'Ogunbanjo', - age: 50, - homeAddress: "Owerri, Nigeria" - ), - ( - firstname: 'Zainab', - lastname: 'Abubakar', - age: 50, - homeAddress: "Lagos, Nigeria" - ), - ( - firstname: 'Chijioke', - lastname: 'Nwachukwu', - age: 54, - homeAddress: "Owerri, Nigeria" - ), - ( - firstname: 'Folake', - lastname: 'Adewale', - age: 55, - homeAddress: "Owerri, Nigeria" - ), - ( - firstname: 'Mustafa', - lastname: 'Olawale', - age: 56, - homeAddress: "Lagos, Nigeria" - ), - ( - firstname: 'Halima', - lastname: 'Idris', - age: 57, - homeAddress: "Lagos, Nigeria" - ), - ( - firstname: 'Chukwuemeka', - lastname: 'Okonkwo', - age: 58, - homeAddress: "Abuja, Nigeria" - ), + (firstname: 'Abdul', lastname: 'Ibrahim', age: 28, homeAddress: "Owerri, Nigeria"), + (firstname: 'Amina', lastname: 'Sule', age: 29, homeAddress: "Owerri, Nigeria"), + (firstname: 'Chukwudi', lastname: 'Okafor', age: 30, homeAddress: "Lagos, Nigeria"), + (firstname: 'Chioma', lastname: 'Nwosu', age: 31, homeAddress: "Lagos, Nigeria"), + (firstname: 'Yusuf', lastname: 'Aliyu', age: 32, homeAddress: "Owerri, Nigeria"), + (firstname: 'Blessing', lastname: 'Okonkwo', age: 33, homeAddress: "Owerri, Nigeria"), + (firstname: 'Tunde', lastname: 'Williams', age: 34, homeAddress: "Abuja, Nigeria"), + (firstname: 'Rukayat', lastname: 'Sanni', age: 35, homeAddress: "Abuja, Nigeria"), + (firstname: 'Segun', lastname: 'Adeleke', age: 36, homeAddress: "Lagos, Nigeria"), + (firstname: 'Abdullahi', lastname: 'Mohammed', age: 46, homeAddress: "Lagos, Nigeria"), + (firstname: 'Chidinma', lastname: 'Onyeka', age: 47, homeAddress: "Owerri, Nigeria"), + (firstname: 'Bola', lastname: 'Akinwumi', age: 48, homeAddress: "Owerri, Nigeria"), + (firstname: 'Haruna', lastname: 'Bello', age: 49, homeAddress: "Lagos, Nigeria"), + (firstname: 'Habiba', lastname: 'Yusuf', age: 50, homeAddress: "Owerri, Nigeria"), + (firstname: 'Tochukwu', lastname: 'Eze', age: 50, homeAddress: "Lagos, Nigeria"), + (firstname: 'Ade', lastname: 'Ogunbanjo', age: 50, homeAddress: "Owerri, Nigeria"), + (firstname: 'Zainab', lastname: 'Abubakar', age: 50, homeAddress: "Lagos, Nigeria"), + (firstname: 'Chijioke', lastname: 'Nwachukwu', age: 54, homeAddress: "Owerri, Nigeria"), + (firstname: 'Folake', lastname: 'Adewale', age: 55, homeAddress: "Owerri, Nigeria"), + (firstname: 'Mustafa', lastname: 'Olawale', age: 56, homeAddress: "Lagos, Nigeria"), + (firstname: 'Halima', lastname: 'Idris', age: 57, homeAddress: "Lagos, Nigeria"), + (firstname: 'Chukwuemeka', lastname: 'Okonkwo', age: 58, homeAddress: "Abuja, Nigeria"), /// Kenyan Users - [9] - ( - firstname: 'Kevin', - lastname: 'Luke', - age: 37, - homeAddress: "Nairobi, Kenya" - ), + (firstname: 'Kevin', lastname: 'Luke', age: 37, homeAddress: "Nairobi, Kenya"), (firstname: 'Foop', lastname: 'Farr', age: 38, homeAddress: "CBD, Kenya"), (firstname: 'Koin', lastname: 'Karl', age: 39, homeAddress: "Mumbasa, Kenya"), (firstname: 'Moo', lastname: 'Maa', age: 40, homeAddress: "Westlands, Kenya"), (firstname: 'Merh', lastname: 'Merh', age: 41, homeAddress: "Nairobi, Kenya"), - ( - firstname: 'Ibrahim', - lastname: 'Bakare', - age: 42, - homeAddress: "Nairobi, Kenya" - ), - ( - firstname: 'Grace', - lastname: 'Adegoke', - age: 43, - homeAddress: "Nairobi, Kenya" - ), - ( - firstname: 'Ahmed', - lastname: 'Umar', - age: 44, - homeAddress: "Nairobi, Kenya" - ), - ( - firstname: 'Nneka', - lastname: 'Okoli', - age: 45, - homeAddress: "Nairobi, Kenya" - ), + (firstname: 'Ibrahim', lastname: 'Bakare', age: 42, homeAddress: "Nairobi, Kenya"), + (firstname: 'Grace', lastname: 'Adegoke', age: 43, homeAddress: "Nairobi, Kenya"), + (firstname: 'Ahmed', lastname: 'Umar', age: 44, homeAddress: "Nairobi, Kenya"), + (firstname: 'Nneka', lastname: 'Okoli', age: 45, homeAddress: "Nairobi, Kenya"), ]; From 3dc8491c17b3eefd2de0752cee07a97dcfa9f953 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 11:16:47 +0000 Subject: [PATCH 31/50] fix formatting --- packages/generator/lib/builder.dart | 3 +-- packages/orm_cli/lib/orm/commands/command.dart | 4 +--- .../lib/orm/commands/migrate_command.dart | 12 ++++-------- .../lib/orm/commands/migrate_reset_command.dart | 6 ++---- .../orm/commands/migrate_rollback_command.dart | 17 +++++------------ packages/orm_cli/lib/src/command_runner.dart | 4 +--- packages/orm_cli/lib/src/migration.g.dart | 15 +++++---------- packages/orm_cli/lib/src/utils.dart | 3 +-- 8 files changed, 20 insertions(+), 44 deletions(-) diff --git a/packages/generator/lib/builder.dart b/packages/generator/lib/builder.dart index da4ffcbd..74d3bba4 100644 --- a/packages/generator/lib/builder.dart +++ b/packages/generator/lib/builder.dart @@ -3,5 +3,4 @@ import 'package:build/build.dart'; import 'src/generator.dart'; /// Builds generators for `build_runner` to run -Builder yaroormBuilder(BuilderOptions options) => - generatorFactoryBuilder(options); +Builder yaroormBuilder(BuilderOptions options) => generatorFactoryBuilder(options); diff --git a/packages/orm_cli/lib/orm/commands/command.dart b/packages/orm_cli/lib/orm/commands/command.dart index 0a6f8f21..a309ce32 100644 --- a/packages/orm_cli/lib/orm/commands/command.dart +++ b/packages/orm_cli/lib/orm/commands/command.dart @@ -35,9 +35,7 @@ abstract class OrmCommand extends Command { final defaultConn = ormConfig.defaultConnName; final args = globalResults; if (args == null) return defaultConn; - return args.wasParsed(OrmCommand.connectionArg) - ? args[OrmCommand.connectionArg] - : defaultConn; + return args.wasParsed(OrmCommand.connectionArg) ? args[OrmCommand.connectionArg] : defaultConn; } List get migrationDefinitions { diff --git a/packages/orm_cli/lib/orm/commands/migrate_command.dart b/packages/orm_cli/lib/orm/commands/migrate_command.dart index f02aec75..73018e3c 100644 --- a/packages/orm_cli/lib/orm/commands/migrate_command.dart +++ b/packages/orm_cli/lib/orm/commands/migrate_command.dart @@ -18,12 +18,10 @@ class MigrateCommand extends OrmCommand { Future execute(DatabaseDriver driver) async { await ensureMigrationsTableReady(driver); - final lastBatchNumber = - await getLastBatchNumber(driver, migrationTableName); + final lastBatchNumber = await getLastBatchNumber(driver, migrationTableName); final batchNos = lastBatchNumber + 1; - logger.info(backgroundBlack - .wrap(' Starting DB migration 📦 \n')); + logger.info(backgroundBlack.wrap(' Starting DB migration 📦 \n')); for (final migration in migrationDefinitions) { final fileName = migration.name; @@ -39,14 +37,12 @@ class MigrateCommand extends OrmCommand { await txnDriver.execute(sql); } - await MigrationQuery.driver(txnDriver) - .create(migration: fileName, batch: batchNos); + await MigrationQuery.driver(txnDriver).create(migration: fileName, batch: batchNos); print('✔ done: $fileName'); }); } - logger.info(backgroundBlack - .wrap('\n Completed DB migration 🚀 \n')); + logger.info(backgroundBlack.wrap('\n Completed DB migration 🚀 \n')); } } diff --git a/packages/orm_cli/lib/orm/commands/migrate_reset_command.dart b/packages/orm_cli/lib/orm/commands/migrate_reset_command.dart index b0688e85..a58b3066 100644 --- a/packages/orm_cli/lib/orm/commands/migrate_reset_command.dart +++ b/packages/orm_cli/lib/orm/commands/migrate_reset_command.dart @@ -18,8 +18,7 @@ class MigrationResetCommand extends OrmCommand { Future execute(DatabaseDriver driver) async { await ensureMigrationsTableReady(driver); - final migrationsList = - await MigrationQuery.driver(driver).orderByDesc('batch').all(); + final migrationsList = await MigrationQuery.driver(driver).orderByDesc('batch').all(); if (migrationsList.isEmpty) { print('𐄂 skipped: reason: no migrations to reset'); return; @@ -28,8 +27,7 @@ class MigrationResetCommand extends OrmCommand { print('------- Resetting migrations 📦 -------\n'); final rollbacks = migrationDefinitions.reversed.map((e) { - final entry = - migrationsList.firstWhereOrNull((entry) => e.name == entry.migration); + final entry = migrationsList.firstWhereOrNull((entry) => e.name == entry.migration); return entry == null ? null : (entry: entry, schemas: e.down); }).whereNotNull(); diff --git a/packages/orm_cli/lib/orm/commands/migrate_rollback_command.dart b/packages/orm_cli/lib/orm/commands/migrate_rollback_command.dart index 5f75da82..543cbd32 100644 --- a/packages/orm_cli/lib/orm/commands/migrate_rollback_command.dart +++ b/packages/orm_cli/lib/orm/commands/migrate_rollback_command.dart @@ -18,18 +18,14 @@ class MigrationRollbackCommand extends OrmCommand { Future execute(DatabaseDriver driver) async { await ensureMigrationsTableReady(driver); - final lastBatchNumber = - await getLastBatchNumber(driver, migrationTableName); + final lastBatchNumber = await getLastBatchNumber(driver, migrationTableName); - final entries = await MigrationEntityQuery.driver(driver) - .equal('batch', lastBatchNumber) - .findMany(); + final entries = await MigrationEntityQuery.driver(driver).equal('batch', lastBatchNumber).findMany(); /// rollbacks start from the last class listed in the migrations list final migrationTask = migrationDefinitions .map((defn) { - final entry = - entries.firstWhereOrNull((e) => e.migration == defn.name); + final entry = entries.firstWhereOrNull((e) => e.migration == defn.name); return entry == null ? null : (entry: entry, schemas: defn.down); }) .whereNotNull() @@ -40,8 +36,7 @@ class MigrationRollbackCommand extends OrmCommand { return; } - print( - '------- Rolling back ${migrationTask.entry.migration} 📦 -------\n'); + print('------- Rolling back ${migrationTask.entry.migration} 📦 -------\n'); await processRollbacks(driver, [migrationTask]); @@ -61,9 +56,7 @@ Future processRollbacks( await transactor.execute(e.toScript(driver.blueprint)); } - await MigrationQuery.driver(transactor) - .equal('id', rollback.entry.id) - .delete(); + await MigrationQuery.driver(transactor).whereId(rollback.entry.id).delete(); }); print('✔ rolled back: ${rollback.entry.migration}'); diff --git a/packages/orm_cli/lib/src/command_runner.dart b/packages/orm_cli/lib/src/command_runner.dart index e1842453..90f9a59e 100644 --- a/packages/orm_cli/lib/src/command_runner.dart +++ b/packages/orm_cli/lib/src/command_runner.dart @@ -10,9 +10,7 @@ class YarooCliCommandRunner extends CompletionCommandRunner { YarooCliCommandRunner() : super(executableName, description) { argParser ..addFlag('version', negatable: false, help: 'Print the current version.') - ..addFlag('verbose', - abbr: 'v', - help: 'Noisy logging, including all shell commands executed.', + ..addFlag('verbose', abbr: 'v', help: 'Noisy logging, including all shell commands executed.', callback: (verbose) { if (verbose) logger.level = Level.verbose; }); diff --git a/packages/orm_cli/lib/src/migration.g.dart b/packages/orm_cli/lib/src/migration.g.dart index bafdc093..68358b0b 100644 --- a/packages/orm_cli/lib/src/migration.g.dart +++ b/packages/orm_cli/lib/src/migration.g.dart @@ -10,8 +10,7 @@ part of 'migration.dart'; Query get MigrationEntityQuery => DB.query(); CreateSchema get MigrationEntitySchema => Schema.fromEntity(); -DBEntity get migration_entityTypeData => - DBEntity( +DBEntity get migration_entityTypeData => DBEntity( "migrations", timestampsEnabled: false, columns: [ @@ -43,15 +42,11 @@ class _$MigrationEntityEntityMirror extends EntityMirror { extension MigrationEntityQueryExtension on Query { WhereClause whereId(int value) => equal("id", value); - WhereClause whereMigration(String value) => - equal("migration", value); - WhereClause whereBatch(int value) => - equal("batch", value); + WhereClause whereMigration(String value) => equal("migration", value); + WhereClause whereBatch(int value) => equal("batch", value); Future findById(int value) => whereId(value).findOne(); - Future findByMigration(String value) => - whereMigration(value).findOne(); - Future findByBatch(int value) => - whereBatch(value).findOne(); + Future findByMigration(String value) => whereMigration(value).findOne(); + Future findByBatch(int value) => whereBatch(value).findOne(); Future create({ required String migration, required int batch, diff --git a/packages/orm_cli/lib/src/utils.dart b/packages/orm_cli/lib/src/utils.dart index 950a091e..9df87fe1 100644 --- a/packages/orm_cli/lib/src/utils.dart +++ b/packages/orm_cli/lib/src/utils.dart @@ -7,6 +7,5 @@ import 'dart:io'; /// exited already. This is useful to prevent Future chains from proceeding /// after you've decided to exit. Future flushThenExit(int status) { - return Future.wait([stdout.close(), stderr.close()]) - .then((_) => exit(status)); + return Future.wait([stdout.close(), stderr.close()]).then((_) => exit(status)); } From ad7259ece8fd9a98528d640a7570364d50ad7cd9 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 11:23:00 +0000 Subject: [PATCH 32/50] tiny fixes --- packages/orm/lib/src/database/database.dart | 8 +++++--- packages/orm/lib/src/database/driver/pgsql_driver.dart | 2 +- packages/orm/lib/src/database/driver/sqlite_driver.dart | 8 ++++---- packages/orm/lib/src/database/entity/converter.dart | 2 +- packages/orm/lib/src/database/entity/entity.dart | 5 ++--- packages/orm/lib/src/migration.dart | 1 - packages/orm/lib/src/primitives/where.dart | 5 +++-- 7 files changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/orm/lib/src/database/database.dart b/packages/orm/lib/src/database/database.dart index d9c2587f..5b125abc 100644 --- a/packages/orm/lib/src/database/database.dart +++ b/packages/orm/lib/src/database/database.dart @@ -1,7 +1,9 @@ -library database; +import 'package:yaroorm/src/utils.dart'; -import 'package:collection/collection.dart'; -import 'package:yaroorm/yaroorm.dart'; +import '../config.dart'; +import '../query/query.dart'; +import './entity/entity.dart'; +import './driver/driver.dart'; class UseDatabaseConnection { final DatabaseConnection info; diff --git a/packages/orm/lib/src/database/driver/pgsql_driver.dart b/packages/orm/lib/src/database/driver/pgsql_driver.dart index c628237f..92cf4c0f 100644 --- a/packages/orm/lib/src/database/driver/pgsql_driver.dart +++ b/packages/orm/lib/src/database/driver/pgsql_driver.dart @@ -4,7 +4,7 @@ import 'package:postgres/postgres.dart' as pg; import '../../migration.dart'; import '../../primitives/serializer.dart'; import '../../query/query.dart'; -import '../entity/entity.dart'; +import '../entity/entity.dart' hide value; import 'driver.dart'; import 'mysql_driver.dart'; diff --git a/packages/orm/lib/src/database/driver/sqlite_driver.dart b/packages/orm/lib/src/database/driver/sqlite_driver.dart index 731546f1..edffd8d9 100644 --- a/packages/orm/lib/src/database/driver/sqlite_driver.dart +++ b/packages/orm/lib/src/database/driver/sqlite_driver.dart @@ -1,14 +1,14 @@ import 'package:meta/meta.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:sqflite_common/sql.dart'; -import 'package:collection/collection.dart'; -import 'package:yaroorm/src/migration.dart'; -import 'package:yaroorm/src/query/aggregates.dart'; +import 'package:yaroorm/src/utils.dart'; +import '../../migration.dart'; import '../../primitives/serializer.dart'; import '../../primitives/where.dart'; +import '../../query/aggregates.dart'; import '../../query/query.dart'; -import '../entity/entity.dart'; +import '../entity/entity.dart' hide value; import 'driver.dart'; final _serializer = const SqliteSerializer(); diff --git a/packages/orm/lib/src/database/entity/converter.dart b/packages/orm/lib/src/database/entity/converter.dart index 463e5387..691490b2 100644 --- a/packages/orm/lib/src/database/entity/converter.dart +++ b/packages/orm/lib/src/database/entity/converter.dart @@ -13,7 +13,7 @@ abstract class EntityTypeConverter { class DateTimeConverter extends EntityTypeConverter { const DateTimeConverter(); - String padValue(value) => value.toString().padLeft(2, '0'); + String padValue(v) => v.toString().padLeft(2, '0'); @override DateTime? fromDbType(String? value) { diff --git a/packages/orm/lib/src/database/entity/entity.dart b/packages/orm/lib/src/database/entity/entity.dart index 9673b5cb..a8b40471 100644 --- a/packages/orm/lib/src/database/entity/entity.dart +++ b/packages/orm/lib/src/database/entity/entity.dart @@ -3,7 +3,6 @@ import 'dart:collection'; import 'package:meta/meta.dart'; -import 'package:recase/recase.dart'; import 'package:meta/meta_meta.dart'; @@ -17,9 +16,9 @@ part 'converter.dart'; // part 'relations.dart'; abstract class Entity { - String get _foreignKeyForModel => '${_type.toString().camelCase}Id'; + // String get _foreignKeyForModel => '${_type.toString().camelCase}Id'; - Type get _type => runtimeType; + // Type get _type => runtimeType; DriverContract _driver = DB.defaultDriver; diff --git a/packages/orm/lib/src/migration.dart b/packages/orm/lib/src/migration.dart index 708fe804..808edea1 100644 --- a/packages/orm/lib/src/migration.dart +++ b/packages/orm/lib/src/migration.dart @@ -2,7 +2,6 @@ library migration; import 'package:meta/meta.dart'; import 'package:recase/recase.dart'; -import 'package:yaroorm/yaroorm.dart'; import 'database/entity/entity.dart'; import 'query/query.dart'; diff --git a/packages/orm/lib/src/primitives/where.dart b/packages/orm/lib/src/primitives/where.dart index 1d34289f..0ecea7f0 100644 --- a/packages/orm/lib/src/primitives/where.dart +++ b/packages/orm/lib/src/primitives/where.dart @@ -1,7 +1,8 @@ // ignore_for_file: constant_identifier_names -import 'package:yaroorm/src/query/aggregates.dart'; -import 'package:yaroorm/yaroorm.dart'; +import '../database/entity/entity.dart' hide value; +import '../query/aggregates.dart'; +import '../query/query.dart'; part '_where_impl.dart'; From d7ab8eb67ee01fce47d178f107c5ad4f9d38445d Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 11:25:36 +0000 Subject: [PATCH 33/50] tiny fix --- melos.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/melos.yaml b/melos.yaml index fef196e9..0076ac5a 100644 --- a/melos.yaml +++ b/melos.yaml @@ -21,7 +21,7 @@ scripts: melos exec -c 1 -- "dart test --coverage=coverage" --fail-fast builder: - run: melos exec --ignore="yaroorm_builder" -- "dart run build_runner build" + run: melos exec --ignore="yaroorm_builder" -- "dart run build_runner build --delete-conflicting-outputs" coverage: run: | From ccc23474471a85c999a3702491845af2ff2c3c5b Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 11:28:40 +0000 Subject: [PATCH 34/50] tiny fix --- .github/workflows/test.yml | 102 ++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 483afb58..8d5fb1a8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,61 +37,61 @@ jobs: melos builder melos analyze - # test: - # name: Test Packages - # runs-on: ubuntu-latest - # services: - # mariadb: - # image: mariadb - # env: - # MYSQL_ROOT_PASSWORD: password - # MARIADB_DATABASE: test_db - # ports: - # - 3000:3306 - # mysql: - # image: mysql - # env: - # MYSQL_ROOT_PASSWORD: password - # MYSQL_DATABASE: test_db - # ports: - # - 3001:3306 - # postgres: - # image: postgres:latest - # env: - # POSTGRES_DB: test_db - # POSTGRES_PASSWORD: password - # POSTGRES_USER: root - # ports: - # - 3002:5432 + test: + name: Test Packages + runs-on: ubuntu-latest + services: + mariadb: + image: mariadb + env: + MYSQL_ROOT_PASSWORD: password + MARIADB_DATABASE: test_db + ports: + - 3000:3306 + mysql: + image: mysql + env: + MYSQL_ROOT_PASSWORD: password + MYSQL_DATABASE: test_db + ports: + - 3001:3306 + postgres: + image: postgres:latest + env: + POSTGRES_DB: test_db + POSTGRES_PASSWORD: password + POSTGRES_USER: root + ports: + - 3002:5432 - # steps: - # - name: Checkout Repository - # uses: actions/checkout@v3 + steps: + - name: Checkout Repository + uses: actions/checkout@v3 - # - uses: dart-lang/setup-dart@v1.3 - # with: - # sdk: "3.0.0" - # - uses: bluefireteam/melos-action@v3 + - uses: dart-lang/setup-dart@v1.3 + with: + sdk: "3.0.0" + - uses: bluefireteam/melos-action@v3 - # - name: Prepare Workspace - # run: | - # dart pub global activate melos - # dart pub global activate coverage - # melos bootstrap - # melos builder + - name: Prepare Workspace + run: | + dart pub global activate melos + dart pub global activate coverage + melos bootstrap + melos builder - # - name: Run Unit Tests - # run: melos tests:ci + - name: Run Unit Tests + run: melos tests:ci - # - name: Run E2E Tests - # run: cd packages/yaroorm && "./e2e_test.sh" + # - name: Run E2E Tests + # run: cd packages/yaroorm && "./e2e_test.sh" - # - name: Combine Coverage - # run: melos coverage + # - name: Combine Coverage + # run: melos coverage - # - name: Upload Coverage - # uses: codecov/codecov-action@v3 - # env: - # CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - # with: - # files: coverage/*_lcov.info + # - name: Upload Coverage + # uses: codecov/codecov-action@v3 + # env: + # CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + # with: + # files: coverage/*_lcov.info From 739491a061f25f31aa46543f8587554e9a5c756e Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 11:37:46 +0000 Subject: [PATCH 35/50] tiny fix --- packages/orm/test/yaroorm_test.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/orm/test/yaroorm_test.dart b/packages/orm/test/yaroorm_test.dart index 875fa80b..fccead09 100644 --- a/packages/orm/test/yaroorm_test.dart +++ b/packages/orm/test/yaroorm_test.dart @@ -11,6 +11,8 @@ Matcher throwsArgumentErrorWithMessage(String message) => throwsA(isA().having((p0) => p0.message, '', message)); void main() { + Query.addTypeDef(userTypeData); + setUpAll(() => DB.init(db.config)); group('DatabaseDriver.init', () { @@ -90,7 +92,7 @@ void main() { test('should err when Query without driver', () async { late Object error; try { - await UserQuery.all(); + await Query.table().all(); } catch (e) { error = e; } From edc82784b49d44bb20beab3d82ae4404e599af4d Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 11:42:58 +0000 Subject: [PATCH 36/50] tiny fix --- packages/generator/test/gen_test.dart | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 packages/generator/test/gen_test.dart diff --git a/packages/generator/test/gen_test.dart b/packages/generator/test/gen_test.dart new file mode 100644 index 00000000..5e3407a2 --- /dev/null +++ b/packages/generator/test/gen_test.dart @@ -0,0 +1,7 @@ +import 'package:test/test.dart'; + +void main() { + test('nothing', () { + // do nothing, hahaha + }); +} From b4ce1648316ffe1f3f4255c7ea87f425c19622b2 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 12:25:33 +0000 Subject: [PATCH 37/50] tiny fix --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8d5fb1a8..b1f1e552 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -83,8 +83,8 @@ jobs: - name: Run Unit Tests run: melos tests:ci - # - name: Run E2E Tests - # run: cd packages/yaroorm && "./e2e_test.sh" + - name: Run E2E Tests + run: cd packages/yaroorm && "./e2e_test.sh" # - name: Combine Coverage # run: melos coverage From 88fd69e77acb448f36b6e1afacc181771b41fbef Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 12:36:01 +0000 Subject: [PATCH 38/50] update tests --- .github/workflows/test.yml | 2 +- packages/orm/test/integration/e2e_basic.dart | 146 ++++++++----------- 2 files changed, 65 insertions(+), 83 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b1f1e552..d258e67f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -84,7 +84,7 @@ jobs: run: melos tests:ci - name: Run E2E Tests - run: cd packages/yaroorm && "./e2e_test.sh" + run: cd packages/orm && "./e2e_test.sh" # - name: Combine Coverage # run: melos coverage diff --git a/packages/orm/test/integration/e2e_basic.dart b/packages/orm/test/integration/e2e_basic.dart index 2ff80d7b..6ec28856 100644 --- a/packages/orm/test/integration/e2e_basic.dart +++ b/packages/orm/test/integration/e2e_basic.dart @@ -109,88 +109,70 @@ void runBasicE2ETest(String connectionName) { expect(userFromDB?.age, 100); }); - // test('should update many users', () async { - // final age50Users = userQuery.equal('age', 50); - // final usersWithAge50 = await age50Users.findMany(); - // expect(usersWithAge50.length, 4); - // expect(usersWithAge50.every((e) => e.age == 50), isTrue); - - // await userQuery.update( - // where: (query) => query.equal('age', 50), - // value: {'home_address': 'Keta, Ghana'}).execute(); - - // final updatedResult = await age50Users.findMany(); - // expect(updatedResult.length, 4); - // expect(updatedResult.every((e) => e.age == 50), isTrue); - // expect( - // updatedResult.every((e) => e.homeAddress == 'Keta, Ghana'), isTrue); - // }); - - // test('should fetch only users in Ghana', () async { - // final query = db - // .query() - // .isLike('home_address', '%, Ghana') - // .orderByDesc('age'); - // final usersInGhana = await query.findMany(); - // expect(usersInGhana.length, 10); - // expect( - // usersInGhana.every((e) => e.homeAddress.contains('Ghana')), - // isTrue, - // ); - - // expect(await query.take(4), hasLength(4)); - // }); - - // test('should get all users between age 35 and 50', () async { - // final age50Users = await db - // .query() - // .isBetween('age', [35, 50]) - // .orderByDesc('age') - // .findMany(); - // expect(age50Users.length, 19); - // expect(age50Users.first.age, 50); - // expect(age50Users.last.age, 35); - // }); - - // test('should get all users in somewhere in Nigeria', () async { - // final users = await db - // .query() - // .isLike('home_address', '%, Nigeria') - // .orderByAsc('home_address') - // .findMany(); - - // expect(users.length, 18); - // expect(users.first.homeAddress, 'Abuja, Nigeria'); - // expect(users.last.homeAddress, 'Owerri, Nigeria'); - // }); - - // test('should get all users where age is 30 or 52', () async { - // final users = await db - // .query() - // .equal('age', 30) - // .orWhere('age', '=', 52) - // .findMany(); - // expect(users.every((e) => [30, 52].contains(e.age)), isTrue); - // }); - - // test('should delete user', () async { - // final userOne = await db.query().get(); - // expect(userOne, isNotNull); - - // await userOne!.delete(); - - // final usersAfterDelete = await db.query().all(); - // expect(usersAfterDelete.any((e) => e.id == userOne.id), isFalse); - // }); - - // test('should delete many users', () async { - // final query = db.query().isLike('home_address', '%, Nigeria'); - // expect(await query.findMany(), isNotEmpty); - - // await query.delete(); - - // expect(await query.findMany(), isEmpty); - // }); + test('should update many users', () async { + final age50Users = userQuery.whereAge(50); + final usersWithAge50 = await age50Users.findMany(); + expect(usersWithAge50.length, 4); + expect(usersWithAge50.every((e) => e.age == 50), isTrue); + + await userQuery.whereAge(50).update(homeAddress: value('Keta, Ghana')); + + final updatedResult = await age50Users.findMany(); + expect(updatedResult.length, 4); + expect(updatedResult.every((e) => e.age == 50), isTrue); + expect(updatedResult.every((e) => e.homeAddress == 'Keta, Ghana'), isTrue); + }); + + test('should fetch only users in Ghana', () async { + final query = UserQuery.isLike('home_address', '%, Ghana').orderByDesc('age'); + final usersInGhana = await query.findMany(); + expect(usersInGhana.length, 10); + expect( + usersInGhana.every((e) => e.homeAddress.contains('Ghana')), + isTrue, + ); + + expect(await query.take(4), hasLength(4)); + }); + + test('should get all users between age 35 and 50', () async { + final age50Users = await UserQuery.isBetween('age', [35, 50]).orderByDesc('age').findMany(); + expect(age50Users.length, 19); + expect(age50Users.first.age, 50); + expect(age50Users.last.age, 35); + }); + + test('should get all users in somewhere in Nigeria', () async { + final users = await UserQuery.isLike('home_address', '%, Nigeria').orderByAsc('home_address').findMany(); + + expect(users.length, 18); + expect(users.first.homeAddress, 'Abuja, Nigeria'); + expect(users.last.homeAddress, 'Owerri, Nigeria'); + }); + + test('should get all users where age is 30 or 52', () async { + final users = await UserQuery.whereAge(30).orWhere('age', '=', 52).findMany(); + expect(users.every((e) => [30, 52].contains(e.age)), isTrue); + }); + + test('should delete user', () async { + final userOne = await UserQuery.get(); + expect(userOne, isNotNull); + + await UserQuery.whereId(userOne!.id).delete(); + + final usersAfterDelete = await UserQuery.all(); + expect(usersAfterDelete.any((e) => e.id == userOne.id), isFalse); + }); + + test('should delete many users', () async { + final query = UserQuery.isLike('home_address', '%, Nigeria'); + expect(await query.findMany(), isNotEmpty); + + await query.delete(); + + expect(await query.findMany(), isEmpty); + }); test('should drop tables', () async { await runMigrator(connectionName, 'migrate:reset'); From 52468700d6b972e30797eec176264a6f40d026ba Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Fri, 12 Apr 2024 13:38:04 +0000 Subject: [PATCH 39/50] updates --- packages/orm/lib/src/query/query.dart | 2 + packages/orm/lib/src/query/query_impl.dart | 11 ++- packages/orm/test/integration/e2e_basic.dart | 85 +++++++++++-------- .../test/integration/fixtures/migrator.dart | 6 ++ 4 files changed, 65 insertions(+), 39 deletions(-) diff --git a/packages/orm/lib/src/query/query.dart b/packages/orm/lib/src/query/query.dart index 9dd7e9ef..6e2d9a02 100644 --- a/packages/orm/lib/src/query/query.dart +++ b/packages/orm/lib/src/query/query.dart @@ -87,6 +87,8 @@ abstract interface class Query extends QueryBase> final List> whereClauses; final DBEntity entity; + Query get _virtual => Query.table(tableName).driver(runner); + Map get converters => combineConverters(entity.converters, runner.typeconverters); static final Map _typedatas = {}; diff --git a/packages/orm/lib/src/query/query_impl.dart b/packages/orm/lib/src/query/query_impl.dart index 8ae9cfb2..e4539a20 100644 --- a/packages/orm/lib/src/query/query_impl.dart +++ b/packages/orm/lib/src/query/query_impl.dart @@ -43,10 +43,15 @@ class QueryImpl extends Query { data[updatedAtField.dartName] = now; } } + final recordId = await runner.insert( + InsertQuery( + tableName, + data: entityMapToDbData(data, converters), + ), + ); - final dataToDbD = entityMapToDbData(data, converters); - final recordId = await runner.insert(InsertQuery(tableName, data: dataToDbD)); - return (await get(recordId))!; + /// TODO: rework this to be a READ QUERY; + return (await _virtual.get(recordId))!; } @override diff --git a/packages/orm/test/integration/e2e_basic.dart b/packages/orm/test/integration/e2e_basic.dart index 6ec28856..153f9aa9 100644 --- a/packages/orm/test/integration/e2e_basic.dart +++ b/packages/orm/test/integration/e2e_basic.dart @@ -7,50 +7,57 @@ import 'fixtures/migrator.dart'; import 'fixtures/test_data.dart'; void runBasicE2ETest(String connectionName) { - final _ = DB.connection(connectionName); - final userQuery = UserQuery.driver(_.driver); + Query.addTypeDef(userTypeData); - return group('with ${_.driver.type.name} driver', () { + final driver = DB.driver(connectionName); + + return group('with ${driver.type.name} driver', () { test('driver should connect', () async { - await _.driver.connect(); + await driver.connect(); - expect(_.driver.isOpen, isTrue); + expect(driver.isOpen, isTrue); }); - test('should have no tables', () async => expect(await _.driver.hasTable('users'), isFalse)); + test('should have no tables', () async => expect(await driver.hasTable('users'), isFalse)); test('should execute migration', () async { await runMigrator(connectionName, 'migrate'); - expect(await _.driver.hasTable('users'), isTrue); + expect(await driver.hasTable('users'), isTrue); }); test('should insert single user', () async { final firstData = usersList.first; - final result = await userQuery.create( + final query = UserQuery.driver(driver); + final result = await query.create( firstname: firstData.firstname, lastname: firstData.lastname, age: firstData.age, homeAddress: firstData.homeAddress, ); - expect(result, isA().having((p0) => p0.id, 'has primary key', 1)); + expect(result.id, 1); - expect(await userQuery.all(), hasLength(1)); + expect(await query.all(), hasLength(1)); }); test('should insert many users', () async { - await Future.wait(usersList - .sublist(1) - .map((e) => - userQuery.create(firstname: e.firstname, lastname: e.lastname, age: e.age, homeAddress: e.homeAddress)) - .toList()); - - expect(await UserQuery.count(), hasLength(usersList.length)); + final query = UserQuery.driver(driver); + + for (final user in usersList.skip(1)) { + await query.create( + firstname: user.firstname, + lastname: user.lastname, + age: user.age, + homeAddress: user.homeAddress, + ); + } + + expect(await query.count(), usersList.length); }); group('Aggregate Functions', () { - final query = userQuery.isLike('home_address', '%%, Ghana'); + final query = UserQuery.driver(driver).isLike('home_address', '%%, Ghana'); List usersInGhana = []; setUpAll(() async { @@ -84,7 +91,7 @@ void runBasicE2ETest(String connectionName) { test('concat', () async { Matcher matcher(String separator) { - if ([DatabaseDriverType.sqlite, DatabaseDriverType.pgsql].contains(_.driver.type)) { + if ([DatabaseDriverType.sqlite, DatabaseDriverType.pgsql].contains(driver.type)) { return equals(usersInGhana.map((e) => e.age).join(separator)); } @@ -98,19 +105,22 @@ void runBasicE2ETest(String connectionName) { }); test('should update user', () async { - final user = await userQuery.get(); + final query = UserQuery.driver(driver); + final user = await query.get(); expect(user!.id, 1); - await userQuery.whereId(user.id).update(firstname: value('Red Oil'), age: value(100)); + await query.whereId(user.id).update(firstname: value('Red Oil'), age: value(100)); - final userFromDB = await userQuery.whereId(user.id).findOne(); + final userFromDB = await query.whereId(user.id).findOne(); expect(user, isNotNull); expect(userFromDB?.firstname, 'Red Oil'); expect(userFromDB?.age, 100); }); test('should update many users', () async { + final userQuery = UserQuery.driver(driver); final age50Users = userQuery.whereAge(50); + final usersWithAge50 = await age50Users.findMany(); expect(usersWithAge50.length, 4); expect(usersWithAge50.every((e) => e.age == 50), isTrue); @@ -124,26 +134,28 @@ void runBasicE2ETest(String connectionName) { }); test('should fetch only users in Ghana', () async { - final query = UserQuery.isLike('home_address', '%, Ghana').orderByDesc('age'); - final usersInGhana = await query.findMany(); + final userQuery = UserQuery.driver(driver).isLike('home_address', '%, Ghana').orderByDesc('age'); + + final usersInGhana = await userQuery.findMany(); expect(usersInGhana.length, 10); expect( usersInGhana.every((e) => e.homeAddress.contains('Ghana')), isTrue, ); - expect(await query.take(4), hasLength(4)); + expect(await userQuery.take(4), hasLength(4)); }); test('should get all users between age 35 and 50', () async { - final age50Users = await UserQuery.isBetween('age', [35, 50]).orderByDesc('age').findMany(); + final age50Users = await UserQuery.driver(driver).isBetween('age', [35, 50]).orderByDesc('age').findMany(); expect(age50Users.length, 19); expect(age50Users.first.age, 50); expect(age50Users.last.age, 35); }); test('should get all users in somewhere in Nigeria', () async { - final users = await UserQuery.isLike('home_address', '%, Nigeria').orderByAsc('home_address').findMany(); + final users = + await UserQuery.driver(driver).isLike('home_address', '%, Nigeria').orderByAsc('home_address').findMany(); expect(users.length, 18); expect(users.first.homeAddress, 'Abuja, Nigeria'); @@ -151,22 +163,23 @@ void runBasicE2ETest(String connectionName) { }); test('should get all users where age is 30 or 52', () async { - final users = await UserQuery.whereAge(30).orWhere('age', '=', 52).findMany(); + final users = await UserQuery.driver(driver).whereAge(30).orWhere('age', '=', 52).findMany(); expect(users.every((e) => [30, 52].contains(e.age)), isTrue); }); test('should delete user', () async { - final userOne = await UserQuery.get(); + final userQuery = UserQuery.driver(driver); + final userOne = await userQuery.get(); expect(userOne, isNotNull); - await UserQuery.whereId(userOne!.id).delete(); + await userQuery.whereId(userOne!.id).delete(); - final usersAfterDelete = await UserQuery.all(); + final usersAfterDelete = await userQuery.all(); expect(usersAfterDelete.any((e) => e.id == userOne.id), isFalse); }); test('should delete many users', () async { - final query = UserQuery.isLike('home_address', '%, Nigeria'); + final query = UserQuery.driver(driver).isLike('home_address', '%, Nigeria'); expect(await query.findMany(), isNotEmpty); await query.delete(); @@ -177,15 +190,15 @@ void runBasicE2ETest(String connectionName) { test('should drop tables', () async { await runMigrator(connectionName, 'migrate:reset'); - expect(await _.driver.hasTable('users'), isFalse); + expect(await driver.hasTable('users'), isFalse); }); test('should disconnect', () async { - expect(_.driver.isOpen, isTrue); + expect(driver.isOpen, isTrue); - await _.driver.disconnect(); + await driver.disconnect(); - expect(_.driver.isOpen, isFalse); + expect(driver.isOpen, isFalse); }); }); } diff --git a/packages/orm/test/integration/fixtures/migrator.dart b/packages/orm/test/integration/fixtures/migrator.dart index 3a6b0e5c..a4f5c534 100644 --- a/packages/orm/test/integration/fixtures/migrator.dart +++ b/packages/orm/test/integration/fixtures/migrator.dart @@ -2,9 +2,15 @@ import 'dart:io'; import 'package:test/test.dart'; import 'package:yaroo_cli/orm.dart'; +import 'package:yaroorm/yaroorm.dart'; import '../fixtures/orm_config.dart' as conf; +import 'models.dart'; void main(List args) async { + Query.addTypeDef(userTypeData); + Query.addTypeDef(postTypeData); + Query.addTypeDef(post_commentTypeData); + await OrmCLIRunner.start(args, conf.config); } From 311a3772041efbdc3f27b046ac65685a65ed5bae Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sat, 13 Apr 2024 15:58:36 +0000 Subject: [PATCH 40/50] new structure working fine --- packages/orm/lib/src/database/database.dart | 2 +- .../orm/lib/src/database/driver/driver.dart | 2 +- .../lib/src/database/driver/mysql_driver.dart | 16 +- .../lib/src/database/driver/pgsql_driver.dart | 4 +- .../src/database/driver/sqlite_driver.dart | 131 +- .../orm/lib/src/primitives/_where_impl.dart | 196 --- .../orm/lib/src/primitives/serializer.dart | 7 +- packages/orm/lib/src/primitives/where.dart | 166 +- packages/orm/lib/src/query/aggregates.dart | 15 +- packages/orm/lib/src/query/query.dart | 310 +++- packages/orm/lib/src/query/query_impl.dart | 263 --- packages/orm/lib/yaroorm.dart | 1 - packages/orm/test/integration/e2e_basic.dart | 50 +- .../orm/test/unit/dialects/sqlite_test.dart | 1538 ++++++++--------- packages/orm/test/yaroorm_test.dart | 2 +- packages/orm_cli/lib/orm/_misc.dart | 2 +- .../orm/commands/migrate_reset_command.dart | 5 +- .../commands/migrate_rollback_command.dart | 12 +- packages/orm_cli/lib/src/migration.g.dart | 35 +- 19 files changed, 1177 insertions(+), 1580 deletions(-) delete mode 100644 packages/orm/lib/src/primitives/_where_impl.dart delete mode 100644 packages/orm/lib/src/query/query_impl.dart diff --git a/packages/orm/lib/src/database/database.dart b/packages/orm/lib/src/database/database.dart index 5b125abc..0dd3ac2e 100644 --- a/packages/orm/lib/src/database/database.dart +++ b/packages/orm/lib/src/database/database.dart @@ -12,7 +12,7 @@ class UseDatabaseConnection { UseDatabaseConnection(this.info) : driver = DB.driver(info.name); Query query([String? table]) { - return Query.table(table).driver(driver)..database = info.database; + return Query.table(table, info.database).driver(driver); } } diff --git a/packages/orm/lib/src/database/driver/driver.dart b/packages/orm/lib/src/database/driver/driver.dart index 8df5f009..e59a95fd 100644 --- a/packages/orm/lib/src/database/driver/driver.dart +++ b/packages/orm/lib/src/database/driver/driver.dart @@ -73,7 +73,7 @@ class DatabaseConnection { mixin DriverContract { /// Perform query on the database - Future>> query(Query query); + Future>> query(ReadQuery query); /// Perform raw query on the database. Future>> rawQuery(String script); diff --git a/packages/orm/lib/src/database/driver/mysql_driver.dart b/packages/orm/lib/src/database/driver/mysql_driver.dart index aba00cf5..996d68ef 100644 --- a/packages/orm/lib/src/database/driver/mysql_driver.dart +++ b/packages/orm/lib/src/database/driver/mysql_driver.dart @@ -67,7 +67,7 @@ final class MySqlDriver implements DatabaseDriver { Future execute(String script) => rawQuery(script); @override - Future>> query(Query query) { + Future>> query(ReadQuery query) { final sql = _serializer.acceptReadQuery(query); return rawQuery(sql); } @@ -92,9 +92,9 @@ final class MySqlDriver implements DatabaseDriver { @override Future insertMany(InsertManyQuery query) async { - for (final value in query.values) { - await insert(InsertQuery(query.tableName, data: value)); - } + // for (final value in query.values) { + // await insert(InsertQuery(query.tableName, data: value)); + // } } @override @@ -140,7 +140,7 @@ class _MysqlTransactor extends DriverTransactor { } @override - Future>> query(Query query) { + Future>> query(ReadQuery query) { final sql = _serializer.acceptReadQuery(query); return rawQuery(sql); } @@ -164,9 +164,9 @@ class _MysqlTransactor extends DriverTransactor { @override Future insertMany(InsertManyQuery query) async { - for (final value in query.values) { - await insert(InsertQuery(query.tableName, data: value)); - } + // for (final value in query.values) { + // await insert(InsertQuery(query.tableName, data: value)); + // } } @override diff --git a/packages/orm/lib/src/database/driver/pgsql_driver.dart b/packages/orm/lib/src/database/driver/pgsql_driver.dart index 92cf4c0f..ff7b4b22 100644 --- a/packages/orm/lib/src/database/driver/pgsql_driver.dart +++ b/packages/orm/lib/src/database/driver/pgsql_driver.dart @@ -78,7 +78,7 @@ final class PostgreSqlDriver implements DatabaseDriver { bool get isOpen => db?.isOpen ?? false; @override - Future>> query(Query query) async { + Future>> query(ReadQuery query) async { final sqlScript = serializer.acceptReadQuery(query); return _execRawQuery(sqlScript); } @@ -174,7 +174,7 @@ class _PgSqlDriverTransactor extends DriverTransactor { } @override - Future>> query(Query query) { + Future>> query(ReadQuery query) { final sql = _pgsqlSerializer.acceptReadQuery(query); return rawQuery(sql); } diff --git a/packages/orm/lib/src/database/driver/sqlite_driver.dart b/packages/orm/lib/src/database/driver/sqlite_driver.dart index edffd8d9..51ba0f9c 100644 --- a/packages/orm/lib/src/database/driver/sqlite_driver.dart +++ b/packages/orm/lib/src/database/driver/sqlite_driver.dart @@ -5,7 +5,6 @@ import 'package:yaroorm/src/utils.dart'; import '../../migration.dart'; import '../../primitives/serializer.dart'; -import '../../primitives/where.dart'; import '../../query/aggregates.dart'; import '../../query/query.dart'; import '../entity/entity.dart' hide value; @@ -69,7 +68,7 @@ final class SqliteDriver implements DatabaseDriver { } @override - Future>> query(Query query) async { + Future>> query(ReadQuery query) async { final sql = _serializer.acceptReadQuery(query); return rawQuery(sql); } @@ -88,15 +87,15 @@ final class SqliteDriver implements DatabaseDriver { @override Future insertMany(InsertManyQuery query) async { - final db = await _getDatabase(); - final batch = db.batch(); + // final db = await _getDatabase(); + // final batch = db.batch(); - for (final entry in query.values) { - final sql = _serializer.acceptInsertQuery(InsertQuery(query.tableName, data: entry)); - batch.rawInsert(sql, entry.values.toList()); - } + // for (final entry in query.values) { + // final sql = _serializer.acceptInsertQuery(InsertQuery(query.tableName, data: entry)); + // batch.rawInsert(sql, entry.values.toList()); + // } - await batch.commit(noResult: true); + // await batch.commit(noResult: true); } @override @@ -139,7 +138,7 @@ class _SqliteTransactor implements DriverTransactor { Future>> rawQuery(String script) => _txn.rawQuery(script); @override - Future>> query(Query query) async { + Future>> query(ReadQuery query) async { final sql = _serializer.acceptReadQuery(query); return rawQuery(sql); } @@ -164,14 +163,14 @@ class _SqliteTransactor implements DriverTransactor { @override Future insertMany(InsertManyQuery query) async { - final batch = _txn.batch(); + // final batch = _txn.batch(); - for (final entry in query.values) { - final sql = _serializer.acceptInsertQuery(InsertQuery(query.tableName, data: entry)); - batch.rawInsert(sql, entry.values.toList()); - } + // for (final entry in query.values) { + // final sql = _serializer.acceptInsertQuery(InsertQuery(query.tableName, data: entry)); + // batch.rawInsert(sql, entry.values.toList()); + // } - await batch.commit(noResult: true); + // await batch.commit(noResult: true); } @override @@ -195,16 +194,16 @@ class SqliteSerializer extends PrimitiveSerializer { queryBuilder.write('SELECT $selection FROM ${escapeStr(aggregate.tableName)}'); /// WHERE - final clauses = aggregate.whereClauses; - if (clauses.isNotEmpty) { - queryBuilder.write(' WHERE ${_serializeWhereClauses(clauses)}'); + final clause = aggregate.whereClause; + if (clause != null) { + queryBuilder.write(' WHERE ${acceptWhereClause(clause)}'); } return '${queryBuilder.toString()}$terminator'; } @override - String acceptReadQuery(Query query) { + String acceptReadQuery(ReadQuery query) { final queryBuilder = StringBuffer(); /// SELECT @@ -213,13 +212,13 @@ class SqliteSerializer extends PrimitiveSerializer { queryBuilder.write('FROM ${escapeStr(query.tableName)}'); /// WHERE - final clauses = query.whereClauses; - if (clauses.isNotEmpty) { - queryBuilder.write(' WHERE ${_serializeWhereClauses(clauses)}'); + final whereClause = query.whereClause; + if (whereClause != null) { + queryBuilder.write(' WHERE ${acceptWhereClause(whereClause)}'); } /// ORDER BY - final orderBys = query.orderByProps; + final orderBys = query.orderByProps ?? {}; if (orderBys.isNotEmpty) { queryBuilder.write(' ORDER BY ${acceptOrderBy(orderBys.toList())}'); } @@ -230,23 +229,45 @@ class SqliteSerializer extends PrimitiveSerializer { queryBuilder.write(' LIMIT ${acceptLimit(limit)}'); } + /// OFFSET + final offset = query.offset; + if (offset != null) { + queryBuilder.write(' OFFSET ${acceptOffset(offset)}'); + } + return '${queryBuilder.toString()}$terminator'; } - String _serializeWhereClauses(List clauses) { - final sb = StringBuffer(); + String acceptWhereClause(WhereClause clause) { + if (clause is WhereClauseValue) { + return acceptWhereClauseValue(clause); + } + + final whereStr = StringBuffer(); + + if (clause.values.isNotEmpty) { + final combiner = clause is $AndGroup ? 'AND' : 'OR'; - final hasDifferentOperators = clauses.map((e) => e.operators).reduce((val, e) => val..addAll(e)).length > 1; + final children = clause.values; + final group = StringBuffer(); - for (final clause in clauses) { - final result = acceptWhereClause(clause, canGroup: hasDifferentOperators); - if (sb.isEmpty) { - sb.write(result); + for (final val in children) { + final res = acceptWhereClause(val); + if (res.isNotEmpty) { + group.write(group.isEmpty ? res : ' $combiner $res'); + } + } + + final result = children.length > 1 ? '(${group.toString()})' : group.toString(); + + if (whereStr.isNotEmpty) { + whereStr.write('$combiner $result'); } else { - sb.write(' ${clause.operator.name} $result'); + whereStr.write(result); } } - return sb.toString(); + + return whereStr.toString(); } @override @@ -294,50 +315,18 @@ class SqliteSerializer extends PrimitiveSerializer { return fields.isEmpty ? 'SELECT * ' : 'SELECT ${fields.map(escapeStr).join(', ')}'; } - @override - String acceptWhereClause(WhereClause clause, {bool canGroup = false}) { - if (clause.children.isEmpty) { - return acceptWhereClauseValue(clause.clauseValue!); - } - - final whereBb = StringBuffer(); - - final List<(LogicalOperator operator, WhereClause clause)> groupMembers = clause.group; - - final shouldAddParenthesis = canGroup && groupMembers.length > 1; - - for (int i = 0; i < groupMembers.length; i++) { - final isLast = i == groupMembers.length - 1; - final member = groupMembers[i]; - final childOperator = member.$1; - final WhereClause childValue = member.$2; - - if (whereBb.isNotEmpty) { - whereBb.write('${childOperator.name} '); - } - - final shouldWrapChild = childValue.group.length > 1; - final childSerialized = acceptWhereClause(childValue); - - /// wrap sub clauses in parenthesis - shouldWrapChild ? whereBb.write('($childSerialized)') : whereBb.write(childSerialized); - - if (!isLast) whereBb.write(' '); - } - - /// wrap the whole clause in parenthesis - return shouldAddParenthesis ? '($whereBb)' : '$whereBb'; - } - @override String acceptOrderBy(List orderBys) { - direction(OrderByDirection dir) => dir == OrderByDirection.asc ? 'ASC' : 'DESC'; + direction(OrderDirection dir) => dir == OrderDirection.asc ? 'ASC' : 'DESC'; return orderBys.map((e) => '${e.field} ${direction(e.direction)}').join(', '); } @override String acceptLimit(int limit) => '$limit'; + @override + String acceptOffset(int offset) => '$offset'; + @override String get terminator => ';'; @@ -352,8 +341,8 @@ class SqliteSerializer extends PrimitiveSerializer { @override String acceptWhereClauseValue(WhereClauseValue clauseValue) { final field = escapeStr(clauseValue.field); - final value = clauseValue.comparer.value; - final valueOperator = clauseValue.comparer.operator; + final value = clauseValue.value; + final valueOperator = clauseValue.operator; final wrapped = acceptPrimitiveValue(value); return switch (valueOperator) { diff --git a/packages/orm/lib/src/primitives/_where_impl.dart b/packages/orm/lib/src/primitives/_where_impl.dart deleted file mode 100644 index 7cc52069..00000000 --- a/packages/orm/lib/src/primitives/_where_impl.dart +++ /dev/null @@ -1,196 +0,0 @@ -part of 'where.dart'; - -class _WhereClauseImpl extends WhereClause { - _WhereClauseImpl(super.query, {super.operator}); - - @override - WhereClause equal(String field, Value value) { - final newChild = _WhereClauseImpl(query) - ..clauseValue = WhereClauseValue( - field, - (operator: Operator.EQUAL, value: value), - ); - children.add((LogicalOperator.AND, newChild)); - return this; - } - - @override - WhereClause notEqual(String field, Value value) { - final newChild = _WhereClauseImpl(query) - ..clauseValue = WhereClauseValue( - field, - (operator: Operator.NOT_EQUAL, value: value), - ); - children.add((LogicalOperator.AND, newChild)); - return this; - } - - @override - WhereClause isIn(String field, List values) { - final newChild = _WhereClauseImpl(query) - ..clauseValue = WhereClauseValue( - field, - (operator: Operator.IN, value: values), - ); - children.add((LogicalOperator.AND, newChild)); - return this; - } - - @override - WhereClause isNotIn(String field, List values) { - final newChild = _WhereClauseImpl(query) - ..clauseValue = WhereClauseValue( - field, - (operator: Operator.NOT_IN, value: values), - ); - children.add((LogicalOperator.AND, newChild)); - return this; - } - - @override - WhereClause isBetween(String field, List values) { - final newChild = _WhereClauseImpl(query) - ..clauseValue = WhereClauseValue( - field, - (operator: Operator.BETWEEN, value: values), - ); - children.add((LogicalOperator.AND, newChild)); - return this; - } - - @override - WhereClause isNotBetween(String field, List values) { - final newChild = _WhereClauseImpl(query) - ..clauseValue = WhereClauseValue( - field, - (operator: Operator.NOT_BETWEEN, value: values), - ); - children.add((LogicalOperator.AND, newChild)); - return this; - } - - @override - WhereClause isLike(String field, String pattern) { - final newChild = _WhereClauseImpl(query) - ..clauseValue = WhereClauseValue( - field, - (operator: Operator.LIKE, value: pattern), - ); - children.add((LogicalOperator.AND, newChild)); - return this; - } - - @override - WhereClause isNotLike(String field, String pattern) { - final newChild = _WhereClauseImpl(query) - ..clauseValue = WhereClauseValue(field, (operator: Operator.NOT_LIKE, value: pattern)); - children.add((LogicalOperator.AND, newChild)); - return this; - } - - @override - WhereClause isNull(String field) { - final newChild = _WhereClauseImpl(query) - ..clauseValue = WhereClauseValue( - field, - (operator: Operator.NULL, value: null), - ); - children.add((LogicalOperator.AND, newChild)); - return this; - } - - @override - WhereClause isNotNull(String field) { - final newChild = _WhereClauseImpl(query) - ..clauseValue = WhereClauseValue( - field, - (operator: Operator.NOT_NULL, value: null), - ); - children.add((LogicalOperator.AND, newChild)); - return this; - } - - @override - Future delete() { - return DeleteQuery(query.tableName, whereClause: this).driver(query.runner).execute(); - } - - @override - Future> findMany() => query.all(); - - @override - Future findOne() => query.get(); - - @override - Future> take(int limit) => query.take(limit); - - @override - WhereClause orderByAsc(String field) { - query.orderByAsc(field); - return this; - } - - @override - WhereClause orderByDesc(String field) { - query.orderByDesc(field); - return this; - } - - @override - WhereClause where( - String field, - String condition, [ - Value? value, - ]) { - final newChild = _WhereClauseImpl(query)..clauseValue = WhereClauseValue.from(field, condition, value); - children.add((LogicalOperator.AND, newChild)); - return this; - } - - @override - WhereClause orWhere( - String field, - String condition, [ - Value? value, - ]) { - final newChild = _WhereClauseImpl(query, operator: LogicalOperator.OR) - ..clauseValue = WhereClauseValue.from(field, condition, value); - query.whereClauses.add(newChild); - return newChild; - } - - @override - Query whereFunc(Function(Query query) builder) { - return query.whereFunc(builder); - } - - @override - Query orWhereFunc(Function(Query query) builder) { - return query.orWhereFunc(builder); - } - - @override - Future average(String field) => AverageAggregate(query, field).get(); - - @override - Future count({String? field, bool distinct = false}) { - return CountAggregate(query, field).get(); - } - - @override - Future max(String field) => MaxAggregate(query, field).get(); - - @override - Future min(String field) => MinAggregate(query, field).get(); - - @override - Future sum(String field) => SumAggregate(query, field).get(); - - @override - Future groupConcat(String field, String separator) { - return GroupConcatAggregate(query, field, separator).get(); - } - - @override - String get statement => query.statement; -} diff --git a/packages/orm/lib/src/primitives/serializer.dart b/packages/orm/lib/src/primitives/serializer.dart index e863cbc8..0bad72a8 100644 --- a/packages/orm/lib/src/primitives/serializer.dart +++ b/packages/orm/lib/src/primitives/serializer.dart @@ -2,14 +2,13 @@ import 'package:yaroorm/src/migration.dart'; import 'package:yaroorm/src/query/aggregates.dart'; import '../query/query.dart'; -import 'where.dart'; abstract class PrimitiveSerializer { const PrimitiveSerializer(); String acceptAggregate(AggregateFunction aggregate); - String acceptReadQuery(Query query); + String acceptReadQuery(ReadQuery query); String acceptUpdateQuery(UpdateQuery query); @@ -19,8 +18,6 @@ abstract class PrimitiveSerializer { String acceptInsertManyQuery(InsertManyQuery query); - String acceptWhereClause(WhereClause clause); - String acceptWhereClauseValue(WhereClauseValue clauseValue); String acceptSelect(List fields); @@ -29,6 +26,8 @@ abstract class PrimitiveSerializer { String acceptLimit(int limit); + String acceptOffset(int offset); + dynamic acceptPrimitiveValue(dynamic value); String acceptForeignKey(TableBlueprint blueprint, ForeignKey key); diff --git a/packages/orm/lib/src/primitives/where.dart b/packages/orm/lib/src/primitives/where.dart index 0ecea7f0..38cf3ffc 100644 --- a/packages/orm/lib/src/primitives/where.dart +++ b/packages/orm/lib/src/primitives/where.dart @@ -1,51 +1,33 @@ // ignore_for_file: constant_identifier_names -import '../database/entity/entity.dart' hide value; -import '../query/aggregates.dart'; -import '../query/query.dart'; +part of '../query/query.dart'; -part '_where_impl.dart'; +typedef WhereBuilder = WhereClause Function(WhereClauseBuilder builder); -typedef WhereBuilder = WhereClause Function( - Query query, -); +mixin WhereOperation { + $AndGroup and(List values) => $AndGroup._(values); -mixin WhereOperation { - WhereClause where( - String field, - String condition, [ - Value? value, - ]); + $OrGroup or(List values) => $OrGroup._(values); - WhereClause orWhere( - String field, - String condition, [ - Value? value, - ]); + WhereClauseValue $equal(String field, T value); - WhereClause equal(String field, Value value); + WhereClauseValue $notEqual(String field, T value); - WhereClause notEqual(String field, Value value); + WhereClauseValue $isNull(String field); - WhereClause isNull(String field); + WhereClauseValue $isNotNull(String field); - WhereClause isNotNull(String field); + WhereClauseValue> $isIn(String field, List values); - WhereClause isIn(String field, List values); + WhereClauseValue> $isNotIn(String field, List values); - WhereClause isNotIn(String field, List values); + WhereClauseValue $isLike(String field, String pattern); - WhereClause isLike(String field, String pattern); + WhereClauseValue $isNotLike(String field, String pattern); - WhereClause isNotLike(String field, String pattern); + WhereClauseValue> $isBetween(String field, List values); - WhereClause isBetween(String field, List values); - - WhereClause isNotBetween(String field, List values); - - Query orWhereFunc(Function(Query query) builder); - - Query whereFunc(Function(Query query) builder); + WhereClauseValue> $isNotBetween(String field, List values); } enum LogicalOperator { AND, OR } @@ -71,37 +53,27 @@ enum Operator { typedef CompareWithValue = ({Operator operator, Value? value}); -Operator _strToOperator(String condition) => switch (condition) { - '=' => Operator.EQUAL, - '!=' => Operator.NOT_EQUAL, - '<' => Operator.LESS_THAN, - '>' => Operator.GREAT_THAN, - '>=' => Operator.GREATER_THAN_OR_EQUAL_TO, - '<=' => Operator.LESS_THEN_OR_EQUAL_TO, - // - 'in' => Operator.IN, - 'not in' => Operator.NOT_IN, - // - 'like' => Operator.LIKE, - 'not like' => Operator.NOT_LIKE, - // - 'null' => Operator.NULL, - 'not null' => Operator.NOT_NULL, - // - 'between' => Operator.BETWEEN, - 'not between' => Operator.NOT_BETWEEN, - _ => throw ArgumentError.value(condition, null, 'Condition $condition is not known') - }; - -class WhereClauseValue { +abstract interface class WhereClause { + final List values; + WhereClause(this.values); +} + +class $AndGroup extends WhereClause { + $AndGroup._(super.values); +} + +class $OrGroup extends WhereClause { + $OrGroup._(super.values); +} + +class WhereClauseValue extends WhereClause { final String field; - final CompareWithValue comparer; + final Operator operator; + final ValueType value; - WhereClauseValue(this.field, this.comparer) { - final operator = comparer.operator; - final value = comparer.value; + WhereClauseValue._(this.field, this.operator, this.value) : super(const []) { if ([Operator.BETWEEN, Operator.NOT_BETWEEN].contains(operator)) { - if (value is! List || value.length != 2) { + if (value is! Iterable || (value as Iterable).length != 2) { throw ArgumentError( '${operator.name} requires a List with length 2 (val1, val2)', '$field ${operator.name} $value', @@ -109,48 +81,58 @@ class WhereClauseValue { } } } +} - factory WhereClauseValue.from(String field, String condition, value) { - final operator = _strToOperator(condition); +final class WhereClauseBuilder with WhereOperation { + WhereClauseBuilder._(); - return WhereClauseValue(field, (operator: operator, value: value)); + @override + WhereClauseValue $equal(String field, V value) { + return WhereClauseValue._(field, Operator.EQUAL, value); } -} - -abstract class WhereClause - with WhereOperation, FindOperation, LimitOperation, OrderByOperation>, AggregateOperation { - final List>> children = []; - List>> get group => children.isEmpty - ? const [] - : [ - if (clauseValue != null) - (operator, WhereClause.create(query, operator: operator)..clauseValue = clauseValue), - ...children - ]; + @override + WhereClauseValue $notEqual(String field, V value) { + return WhereClauseValue._(field, Operator.NOT_EQUAL, value); + } - Set get operators => { - operator, - if (children.isNotEmpty) ...children.map((e) => e.$1), - }; + @override + WhereClauseValue> $isIn(String field, List values) { + return WhereClauseValue._(field, Operator.IN, values); + } - final Query query; + @override + WhereClauseValue> $isNotIn(String field, List values) { + return WhereClauseValue._(field, Operator.NOT_IN, values); + } - final LogicalOperator operator; + @override + WhereClauseValue $isLike(String field, String pattern) { + return WhereClauseValue._(field, Operator.LIKE, pattern); + } - WhereClauseValue? clauseValue; + @override + WhereClauseValue $isNotLike(String field, String pattern) { + return WhereClauseValue._(field, Operator.NOT_LIKE, pattern); + } - WhereClause(this.query, {this.operator = LogicalOperator.AND}); + @override + WhereClauseValue $isNull(String field) { + return WhereClauseValue._(field, Operator.NULL, null); + } - static WhereClause create( - Query query, { - LogicalOperator operator = LogicalOperator.AND, - WhereClauseValue? value, - }) { - return _WhereClauseImpl(query, operator: operator)..clauseValue = value; + @override + WhereClauseValue $isNotNull(String field) { + return WhereClauseValue._(field, Operator.NOT_NULL, null); } - Future delete(); + @override + WhereClauseValue> $isBetween(String field, List values) { + return WhereClauseValue._(field, Operator.BETWEEN, values); + } - String get statement; + @override + WhereClauseValue> $isNotBetween(String field, List values) { + return WhereClauseValue._(field, Operator.NOT_BETWEEN, values); + } } diff --git a/packages/orm/lib/src/query/aggregates.dart b/packages/orm/lib/src/query/aggregates.dart index 4f84eac8..dd848e28 100644 --- a/packages/orm/lib/src/query/aggregates.dart +++ b/packages/orm/lib/src/query/aggregates.dart @@ -1,10 +1,9 @@ import '../database/driver/driver.dart'; -import '../primitives/where.dart'; import 'query.dart'; sealed class AggregateFunction { final List selections; - final List whereClauses; + final WhereClause? whereClause; final String tableName; final DriverContract driver; @@ -19,17 +18,17 @@ sealed class AggregateFunction { this.driver, this.tableName, { this.selections = const [], - this.whereClauses = const [], + this.whereClause, }); - AggregateFunction._init(Query query, String? field) + AggregateFunction._init(ReadQuery query, String? field) : driver = query.runner, tableName = query.tableName, - whereClauses = query.whereClauses, + whereClause = query.whereClause, selections = field == null ? const [] : [field], assert( query.fieldSelections.isEmpty, - 'You can not use selections with aggregate functions', + 'You can not use selections with aggregate functisons', ); Future get() async { @@ -48,7 +47,9 @@ sealed class AggregateFunction { } class CountAggregate extends AggregateFunction { - CountAggregate(super.query, super.field) : super._init(); + final bool distinct; + + CountAggregate(super.query, super.field, this.distinct) : super._init(); @override String get name => 'COUNT'; diff --git a/packages/orm/lib/src/query/query.dart b/packages/orm/lib/src/query/query.dart index 6e2d9a02..77e258a2 100644 --- a/packages/orm/lib/src/query/query.dart +++ b/packages/orm/lib/src/query/query.dart @@ -1,36 +1,34 @@ import 'package:meta/meta.dart'; import '../database/driver/driver.dart'; -import '../database/entity/entity.dart'; -import '../primitives/where.dart'; +import '../database/entity/entity.dart' hide value; import '../reflection.dart'; import 'aggregates.dart'; -part 'query_impl.dart'; +part '../primitives/where.dart'; -mixin ReadOperation { - Future get([Object id]); +enum OrderDirection { asc, desc } - Future> all({int? limit}); - - Future> take(int limit); -} - -mixin FindOperation { - Future findOne(); +mixin ReadOperation { + Future findOne({ + WhereBuilder? where, + }); - Future> findMany(); + Future> findMany({ + WhereBuilder? where, + List> orderBy, + int? limit, + int? offset, + }); } mixin InsertOperation { Future $insert(Map data); - - Future $insertMany(List> values); } mixin UpdateOperation { UpdateQuery $update({ - required WhereClause Function(Query query) where, + required WhereBuilder where, required Map values, }); } @@ -39,87 +37,63 @@ mixin LimitOperation { Future> take(int limit); } -typedef OrderBy = ({String field, OrderByDirection direction}); - -mixin OrderByOperation { - ReturnType orderByAsc(String field); +abstract class OrderBy { + final String field; + final OrderDirection direction; - ReturnType orderByDesc(String field); + const OrderBy(this.field, this.direction); } sealed class QueryBase { final String tableName; + final String? database; - String? database; + final Query _query; - DriverContract? _queryDriver; - - DriverContract get runner { - if (_queryDriver == null) { - throw StateError('Driver not set for query. Make sure you supply a driver using .driver()'); - } - return _queryDriver!; - } - - Owner driver(DriverContract driver) { - _queryDriver = driver; - return this as Owner; - } + DriverContract get runner => _query.runner; Future execute(); - QueryBase(this.tableName); + QueryBase(this._query) + : tableName = _query.tableName, + database = _query.database; String get statement; } -abstract interface class Query extends QueryBase> - with - ReadOperation, - WhereOperation, - LimitOperation, - OrderByOperation>, - InsertOperation, - UpdateOperation, - AggregateOperation { - final Set fieldSelections; - final Set orderByProps; - final List> whereClauses; +final class Query with ReadOperation, InsertOperation, UpdateOperation, AggregateOperation { final DBEntity entity; + final String? database; - Query get _virtual => Query.table(tableName).driver(runner); + late final String tableName; + + DriverContract? _queryDriver; Map get converters => combineConverters(entity.converters, runner.typeconverters); static final Map _typedatas = {}; - // ignore: prefer_final_fields - int? _limit; - - int? get limit => _limit; - - Query(super.tableName) - : entity = Query.getEntity(), - fieldSelections = {}, - orderByProps = {}, - whereClauses = [], - _limit = null; + Query._({String? tableName, this.database}) : entity = Query.getEntity() { + this.tableName = tableName ?? entity.tableName; + } - static Query table([String? tableName]) { - if (Model != Entity && Model != dynamic) { - tableName ??= getEntityTableName(); + DriverContract get runner { + if (_queryDriver == null) { + throw StateError('Driver not set for query. Make sure you supply a driver using .driver()'); } - assert(tableName != null, 'Either provide Entity Type or tableName'); - return QueryImpl(tableName!); + return _queryDriver!; } - Query select(List selections) { - fieldSelections.addAll(selections); + Query driver(DriverContract driver) { + _queryDriver = driver; return this; } - Future accept>(A query) async { - return (query..driver(runner)).execute(); + static Query table([String? tableName, String? database]) { + if (Model == Entity || Model == dynamic) { + throw UnsupportedError('Query cannot receive Entity or dynamic as Type'); + } + return Query._(tableName: tableName, database: database); } static void addTypeDef(DBEntity entity) { @@ -137,6 +111,123 @@ abstract interface class Query extends QueryBase> } return _typedatas[type]! as DBEntity; } + + ReadQuery where(WhereBuilder builder) { + final whereClause = builder.call(WhereClauseBuilder._()); + return ReadQuery._(this, whereClause: whereClause); + } + + @override + Future $insert(Map data) async { + if (entity.timestampsEnabled) { + final now = DateTime.now(); + final createdAtField = entity.createdAtField; + final updatedAtField = entity.updatedAtField; + + if (createdAtField != null) { + data[createdAtField.dartName] = now; + } + + if (updatedAtField != null) { + data[updatedAtField.dartName] = now; + } + } + final recordId = await runner.insert( + InsertQuery(this, data: entityMapToDbData(data, converters)), + ); + + return (await findOne(where: (q) => q.$equal(entity.primaryKey.columnName, recordId)))!; + } + + @override + Future> findMany({ + WhereBuilder? where, + List>? orderBy, + int? limit, + int? offset, + }) async { + final whereClause = where?.call(WhereClauseBuilder._()); + final readQ = ReadQuery._( + this, + limit: limit, + offset: offset, + whereClause: whereClause, + orderByProps: orderBy?.toSet(), + ); + + final results = await runner.query(readQ); + if (results.isEmpty) return []; + return results.map(_wrapRawResult).toList(); + } + + @override + Future findOne({WhereBuilder? where}) async { + final whereClause = where?.call(WhereClauseBuilder._()); + final readQ = ReadQuery._(this, limit: 1, whereClause: whereClause); + final results = await runner.query(readQ); + if (results.isEmpty) return null; + return results.map(_wrapRawResult).first; + } + + @override + UpdateQuery $update({ + required WhereBuilder where, + required Map values, + }) { + final whereClause = where.call(WhereClauseBuilder._()); + + if (entity.timestampsEnabled) { + final now = DateTime.now(); + final updatedAtField = entity.updatedAtField; + if (updatedAtField != null) { + values[updatedAtField.dartName] = now; + } + } + + final dataToDbD = entityMapToDbData( + values, + converters, + onlyPropertiesPassed: true, + ); + + return UpdateQuery(this, whereClause: whereClause, data: dataToDbD); + } + + /// [T] is the expected type passed to [Query] via Query + T _wrapRawResult(Map? result) { + if (result == null) return result as dynamic; + return serializedPropsToEntity( + result, + entity, + converters, + ); + } + + ReadQuery get _readQuery => ReadQuery._(this); + + @override + Future average(String field) { + return AverageAggregate(_readQuery, field).get(); + } + + @override + Future count({String? field, bool distinct = false}) { + return CountAggregate(_readQuery, field, distinct).get(); + } + + @override + Future groupConcat(String field, String separator) { + return GroupConcatAggregate(_readQuery, field, separator).get(); + } + + @override + Future max(String field) => MaxAggregate(_readQuery, field).get(); + + @override + Future min(String field) => MinAggregate(_readQuery, field).get(); + + @override + Future sum(String field) => SumAggregate(_readQuery, field).get(); } mixin AggregateOperation { @@ -154,20 +245,89 @@ mixin AggregateOperation { } @protected -class UpdateQuery extends QueryBase { +final class UpdateQuery extends QueryBase { final WhereClause whereClause; final Map data; UpdateQuery(super.tableName, {required this.whereClause, required this.data}); @override - String get statement => runner.serializer.acceptUpdateQuery(this); + String get statement => _query.runner.serializer.acceptUpdateQuery(this); @override Future execute() => runner.update(this); } -class InsertQuery extends QueryBase { +final class ReadQuery extends QueryBase with AggregateOperation { + final Set fieldSelections; + final Set>? orderByProps; + final WhereClause? whereClause; + final int? limit, offset; + + final Query $query; + + ReadQuery._( + this.$query, { + this.whereClause, + this.orderByProps, + this.fieldSelections = const {}, + this.limit, + this.offset, + }) : super($query); + + @override + Future>> execute() => runner.query(this); + + @override + String get statement => runner.serializer.acceptReadQuery(this); + + @override + Future average(String field) { + return AverageAggregate(this, field).get(); + } + + @override + Future count({String? field, bool distinct = false}) { + return CountAggregate(this, field, distinct).get(); + } + + @override + Future groupConcat(String field, String separator) { + return GroupConcatAggregate(this, field, separator).get(); + } + + @override + Future max(String field) => MaxAggregate(this, field).get(); + + @override + Future min(String field) => MinAggregate(this, field).get(); + + @override + Future sum(String field) { + return SumAggregate(this, field).get(); + } + + Future> findMany({ + int? limit, + int? offset, + List>? orderBy, + }) { + return $query.findMany( + limit: limit, + offset: offset, + where: (_) => whereClause!, + orderBy: orderBy, + ); + } + + Future findOne() => $query.findOne(where: (_) => whereClause!); + + Future delete() async { + return DeleteQuery(_query, whereClause: whereClause!).execute(); + } +} + +final class InsertQuery extends QueryBase { final Map data; InsertQuery(super.tableName, {required this.data}); @@ -179,7 +339,7 @@ class InsertQuery extends QueryBase { String get statement => runner.serializer.acceptInsertQuery(this); } -class InsertManyQuery extends QueryBase { +final class InsertManyQuery extends QueryBase { final List> values; InsertManyQuery(super.tableName, {required this.values}); @@ -192,10 +352,10 @@ class InsertManyQuery extends QueryBase { } @protected -class DeleteQuery extends QueryBase { +final class DeleteQuery extends QueryBase { final WhereClause whereClause; - DeleteQuery(super.tableName, {required this.whereClause}); + DeleteQuery(super._query, {required this.whereClause}); @override String get statement => runner.serializer.acceptDeleteQuery(this); diff --git a/packages/orm/lib/src/query/query_impl.dart b/packages/orm/lib/src/query/query_impl.dart deleted file mode 100644 index e4539a20..00000000 --- a/packages/orm/lib/src/query/query_impl.dart +++ /dev/null @@ -1,263 +0,0 @@ -part of 'query.dart'; - -enum OrderByDirection { asc, desc } - -class QueryImpl extends Query { - QueryImpl(super.tableName); - - @override - WhereClause where( - String field, - String condition, [ - Value? value, - ]) { - final newClause = WhereClause.create(this, value: WhereClauseValue.from(field, condition, value)); - whereClauses.add(newClause); - return newClause; - } - - @override - Query orderByAsc(String field) { - orderByProps.add((field: field, direction: OrderByDirection.asc)); - return this; - } - - @override - Query orderByDesc(String field) { - orderByProps.add((field: field, direction: OrderByDirection.desc)); - return this; - } - - @override - Future $insert(Map data) async { - if (entity.timestampsEnabled) { - final now = DateTime.now(); - final createdAtField = entity.createdAtField; - final updatedAtField = entity.updatedAtField; - - if (createdAtField != null) { - data[createdAtField.dartName] = now; - } - - if (updatedAtField != null) { - data[updatedAtField.dartName] = now; - } - } - final recordId = await runner.insert( - InsertQuery( - tableName, - data: entityMapToDbData(data, converters), - ), - ); - - /// TODO: rework this to be a READ QUERY; - return (await _virtual.get(recordId))!; - } - - @override - UpdateQuery $update({ - required WhereClause Function(Query query) where, - required Map values, - }) { - if (entity.timestampsEnabled) { - final now = DateTime.now(); - final updatedAtField = entity.updatedAtField; - if (updatedAtField != null) { - values[updatedAtField.dartName] = now; - } - } - - final dataToDbD = entityMapToDbData( - values, - converters, - onlyPropertiesPassed: true, - ); - return UpdateQuery(tableName, whereClause: where(this), data: dataToDbD).driver(runner); - } - - @override - Future $insertMany(List> values) async { - throw Exception(); - } - - @override - Future> all({int? limit}) async { - final results = await runner.query(this.._limit = limit); - if (results.isEmpty) return []; - return results.map(_wrapRawResult).toList(); - } - - @override - Future> take(int limit) async { - final results = await runner.query(this.._limit = limit); - if (results.isEmpty) return []; - return results.map(_wrapRawResult).toList(); - } - - @override - Future get([dynamic id]) async { - if (id != null) { - return equal(getEntityPrimaryKey(), id).findOne(); - } - return (await take(1)).firstOrNull; - } - - /// [T] is the expected type passed to [Query] via Query - Model _wrapRawResult(Map? result) { - if (result == null) return result as dynamic; - return serializedPropsToEntity( - result, - entity, - converters, - ); - } - - @override - WhereClause orWhere(String field, String condition, [Value? value]) { - throw StateError('Cannot use `orWhere` directly on a Query you need a WHERE clause first'); - } - - @override - Query orWhereFunc(Function(Query query) builder) { - if (whereClauses.isEmpty) { - throw StateError('Cannot use `orWhereFunc` without a where clause'); - } - - final newQuery = QueryImpl(tableName); - builder(newQuery); - - final newGroup = WhereClause.create(this, operator: LogicalOperator.OR); - for (final clause in newQuery.whereClauses) { - newGroup.children.add((clause.operator, clause)); - } - - whereClauses.add(newGroup); - - return this; - } - - @override - Query whereFunc(Function(Query query) builder) { - final newQuery = QueryImpl(tableName); - builder(newQuery); - - final newGroup = WhereClause.create(this, operator: LogicalOperator.AND); - for (final clause in newQuery.whereClauses) { - newGroup.children.add((clause.operator, clause)); - } - - whereClauses.add(newGroup); - - return this; - } - - @override - WhereClause equal(String field, Value value) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.EQUAL, value: value))); - whereClauses.add(newClause); - return newClause; - } - - @override - WhereClause notEqual(String field, Value value) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NOT_EQUAL, value: value))); - whereClauses.add(newClause); - return newClause; - } - - @override - WhereClause isIn(String field, List values) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.IN, value: values))); - whereClauses.add(newClause); - return newClause; - } - - @override - WhereClause isNotIn(String field, List values) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NOT_IN, value: values))); - whereClauses.add(newClause); - return newClause; - } - - @override - WhereClause isLike(String field, String pattern) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.LIKE, value: pattern))); - whereClauses.add(newClause); - return newClause; - } - - @override - WhereClause isNotLike(String field, String pattern) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NOT_LIKE, value: pattern))); - whereClauses.add(newClause); - return newClause; - } - - @override - WhereClause isNull(String field) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NULL, value: null))); - whereClauses.add(newClause); - return newClause; - } - - @override - WhereClause isNotNull(String field) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.NOT_NULL, value: null))); - whereClauses.add(newClause); - return newClause; - } - - @override - WhereClause isBetween(String field, List values) { - final newClause = - WhereClause.create(this, value: WhereClauseValue(field, (operator: Operator.BETWEEN, value: values))); - whereClauses.add(newClause); - return newClause; - } - - @override - WhereClause isNotBetween(String field, List values) { - final newClause = WhereClause.create(this, - value: WhereClauseValue(field, (operator: Operator.NOT_BETWEEN, value: values))); - whereClauses.add(newClause); - return newClause; - } - - @override - Future execute() => runner.execute(statement); - - @override - String get statement => runner.serializer.acceptReadQuery(this); - - @override - Future average(String field) { - return AverageAggregate(this, field).get(); - } - - @override - Future count({String? field, bool distinct = false}) { - return CountAggregate(this, field).get(); - } - - @override - Future groupConcat(String field, String separator) { - return GroupConcatAggregate(this, field, separator).get(); - } - - @override - Future max(String field) => MaxAggregate(this, field).get(); - - @override - Future min(String field) => MinAggregate(this, field).get(); - - @override - Future sum(String field) => SumAggregate(this, field).get(); -} diff --git a/packages/orm/lib/yaroorm.dart b/packages/orm/lib/yaroorm.dart index c36885e0..dd736135 100644 --- a/packages/orm/lib/yaroorm.dart +++ b/packages/orm/lib/yaroorm.dart @@ -1,7 +1,6 @@ library; export 'src/query/query.dart'; -export 'src/primitives/where.dart' show WhereClause; export 'src/database/driver/driver.dart'; export 'src/database/entity/entity.dart' hide entityMapToDbData, entityToDbData, serializedPropsToEntity; export 'src/database/database.dart'; diff --git a/packages/orm/test/integration/e2e_basic.dart b/packages/orm/test/integration/e2e_basic.dart index 153f9aa9..9b814f19 100644 --- a/packages/orm/test/integration/e2e_basic.dart +++ b/packages/orm/test/integration/e2e_basic.dart @@ -38,7 +38,7 @@ void runBasicE2ETest(String connectionName) { expect(result.id, 1); - expect(await query.all(), hasLength(1)); + expect(await query.findMany(), hasLength(1)); }); test('should insert many users', () async { @@ -57,7 +57,7 @@ void runBasicE2ETest(String connectionName) { }); group('Aggregate Functions', () { - final query = UserQuery.driver(driver).isLike('home_address', '%%, Ghana'); + final query = UserQuery.driver(driver).where((user) => user.$isLike('home_address', '%%, Ghana')); List usersInGhana = []; setUpAll(() async { @@ -105,13 +105,14 @@ void runBasicE2ETest(String connectionName) { }); test('should update user', () async { - final query = UserQuery.driver(driver); - final user = await query.get(); + final query = UserQuery.driver(driver).where((user) => user.id(1)); + + final user = await query.findOne(); expect(user!.id, 1); - await query.whereId(user.id).update(firstname: value('Red Oil'), age: value(100)); + await query.update(firstname: value('Red Oil'), age: value(100)); - final userFromDB = await query.whereId(user.id).findOne(); + final userFromDB = await query.findOne(); expect(user, isNotNull); expect(userFromDB?.firstname, 'Red Oil'); expect(userFromDB?.age, 100); @@ -119,13 +120,13 @@ void runBasicE2ETest(String connectionName) { test('should update many users', () async { final userQuery = UserQuery.driver(driver); - final age50Users = userQuery.whereAge(50); + final age50Users = userQuery.where((user) => user.age(50)); final usersWithAge50 = await age50Users.findMany(); expect(usersWithAge50.length, 4); expect(usersWithAge50.every((e) => e.age == 50), isTrue); - await userQuery.whereAge(50).update(homeAddress: value('Keta, Ghana')); + await age50Users.update(homeAddress: value('Keta, Ghana')); final updatedResult = await age50Users.findMany(); expect(updatedResult.length, 4); @@ -134,7 +135,7 @@ void runBasicE2ETest(String connectionName) { }); test('should fetch only users in Ghana', () async { - final userQuery = UserQuery.driver(driver).isLike('home_address', '%, Ghana').orderByDesc('age'); + final userQuery = UserQuery.driver(driver).where((user) => user.$isLike('home_address', '%, Ghana')); final usersInGhana = await userQuery.findMany(); expect(usersInGhana.length, 10); @@ -143,19 +144,23 @@ void runBasicE2ETest(String connectionName) { isTrue, ); - expect(await userQuery.take(4), hasLength(4)); + expect(await userQuery.findMany(limit: 4), hasLength(4)); }); test('should get all users between age 35 and 50', () async { - final age50Users = await UserQuery.driver(driver).isBetween('age', [35, 50]).orderByDesc('age').findMany(); + final age50Users = await UserQuery.driver(driver) + .where((user) => user.$isBetween('age', [35, 50])) + .findMany(orderBy: [OrderUserBy.age(OrderDirection.desc)]); + expect(age50Users.length, 19); expect(age50Users.first.age, 50); expect(age50Users.last.age, 35); }); test('should get all users in somewhere in Nigeria', () async { - final users = - await UserQuery.driver(driver).isLike('home_address', '%, Nigeria').orderByAsc('home_address').findMany(); + final users = await UserQuery.driver(driver) + .where((user) => user.$isLike('home_address', '%, Nigeria')) + .findMany(orderBy: [OrderUserBy.homeAddress(OrderDirection.asc)]); expect(users.length, 18); expect(users.first.homeAddress, 'Abuja, Nigeria'); @@ -163,23 +168,30 @@ void runBasicE2ETest(String connectionName) { }); test('should get all users where age is 30 or 52', () async { - final users = await UserQuery.driver(driver).whereAge(30).orWhere('age', '=', 52).findMany(); + final users = await UserQuery.driver(driver) + .where((user) => user.or([ + user.age(30), + user.age(52), + ])) + .findMany(); + expect(users.every((e) => [30, 52].contains(e.age)), isTrue); }); test('should delete user', () async { final userQuery = UserQuery.driver(driver); - final userOne = await userQuery.get(); + final userOne = await userQuery.findOne(); expect(userOne, isNotNull); - await userQuery.whereId(userOne!.id).delete(); + final userOneQuery = userQuery.where((user) => user.id(userOne!.id)); + + await userOneQuery.delete(); - final usersAfterDelete = await userQuery.all(); - expect(usersAfterDelete.any((e) => e.id == userOne.id), isFalse); + expect(await userOneQuery.findOne(), isNull); }); test('should delete many users', () async { - final query = UserQuery.driver(driver).isLike('home_address', '%, Nigeria'); + final query = UserQuery.driver(driver).where((user) => user.$isLike('home_address', '%, Nigeria')); expect(await query.findMany(), isNotEmpty); await query.delete(); diff --git a/packages/orm/test/unit/dialects/sqlite_test.dart b/packages/orm/test/unit/dialects/sqlite_test.dart index b239721d..074124d2 100644 --- a/packages/orm/test/unit/dialects/sqlite_test.dart +++ b/packages/orm/test/unit/dialects/sqlite_test.dart @@ -28,7 +28,7 @@ class ArticleComment extends Entity { ArticleComment(this.id, this.articleId, this.userId); } -void main() { +void main() async { DB.init(db.config); Query.addTypeDef(userTypeData); @@ -41,834 +41,720 @@ void main() { setUpAll(() => driver = DB.driver('foo_sqlite')); - group('SQLITE Query Builder', () { - test('when query', () { - final query = UserQuery.driver(driver); + // print(result); - expect(query.statement, 'SELECT * FROM users;'); - }); - - test('when query with single orderBy', () { - final query = UserQuery.driver(driver).orderByDesc('names'); + // group('SQLITE Query Builder', () { + // test('when query', () { + // final query = UserQuery.driver(driver); - expect(query.statement, 'SELECT * FROM users ORDER BY names DESC;'); - }); + // expect(query.statement, 'SELECT * FROM users;'); + // }); + + // test('when query with single orderBy', () { + // final query = UserQuery.driver(driver).orderByDesc('names'); + + // expect(query.statement, 'SELECT * FROM users ORDER BY names DESC;'); + // }); - test('when query with multiple orderBy', () { - final query = UserQuery.driver(driver).orderByDesc('names').orderByAsc('ages'); - - expect(query.statement, 'SELECT * FROM users ORDER BY names DESC, ages ASC;'); - }); - - // test('when update', () { - // final query = UserQuery.driver(driver).update( - // where: (where) => where.where('name', '=', 'Chima'), - // values: {'firstname': 'Chima', 'lastname': 'Precious'}, - // ); - - // expect(query.statement, - // 'UPDATE users SET firstname = ?, lastname = ? WHERE name = \'Chima\';'); - // }); - - // test('when delete', () { - // final query = UserQuery - // .driver(driver) - // .delete((where) => where.where('name', '=', 'Chima')); - - // expect(query.statement, 'DELETE FROM users WHERE name = \'Chima\';'); - // }); - - group('when .where', () { - test('of level 1', () { - final query = UserQuery.driver(driver).where('firstname', '=', 'Chima'); - - expect(query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\';'); - }); - - test('of level 2', () { - final query = UserQuery.driver(driver).whereFirstname('Chima').where('lastname', '=', 'Precious'); - - expect(query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname = \'Precious\';'); - }); - - test('of level 3', () { - final query = - UserQuery.driver(driver).whereFirstname('Chima').equal('lastname', 'Precious').where('age', '=', 22); - - expect(query.statement, - 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname = \'Precious\' AND age = 22;'); - }); - - group('chained with `.orWhere`', () { - test('of level 1', () { - final query = UserQuery.driver(driver).whereFirstname('Chima').orWhere('age', '=', 203); - - expect(query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\' OR age = 203;'); - }); - - test('of level 2', () { - final query = - UserQuery.driver(driver).whereFirstname('Chima').orWhere('age', '=', 203).where('city', '!=', 'Accra'); - - expect( - query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\' OR (age = 203 AND city != \'Accra\');'); - }); - - test('of level 3', () { - final query = - UserQuery.driver(driver).where('votes', '>', 100).orWhere('name', '=', 'Abigail').where('votes', '>', 50); - - expect(query.statement, 'SELECT * FROM users WHERE votes > 100 OR (name = \'Abigail\' AND votes > 50);'); - }); - - test('of level 4', () { - final query = UserQuery.driver(driver) - .where('firstname', '=', 'Chima') - .orWhere('age', '=', 203) - .orWhere('city', '!=', 'Accra') - .where('name', 'like', 'Chima%'); - - expect( - query.statement, - 'SELECT * FROM users WHERE firstname = \'Chima\' OR age = 203 OR (city != \'Accra\' AND name LIKE \'Chima%\');', - ); - }); - - test('of level 5', () { - final query = UserQuery.driver(driver) - .where('firstname', '=', 'Chima') - .orWhere('age', '=', 203) - .orWhere('city', '!=', 'Accra') - .where('name', 'like', 'Chima%') - .where('sizes', 'between', [12, 23]); - - expect( - query.statement, - 'SELECT * FROM users WHERE firstname = \'Chima\' OR age = 203 OR (city != \'Accra\' AND name LIKE \'Chima%\' AND sizes BETWEEN 12 AND 23);', - ); - }); - }); - - group('chained with', () { - test('.whereNull', () { - final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isNull('age'); - - expect( - query.statement, - 'SELECT * FROM users WHERE firstname = \'Chima\' AND age IS NULL;', - ); - }); - - test('.whereNotNull', () { - final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isNotNull('age'); - - expect( - query.statement, - 'SELECT * FROM users WHERE firstname = \'Chima\' AND age IS NOT NULL;', - ); - }); - - test('.whereIn', () { - final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isIn('age', [22, 24, 25]); - - expect( - query.statement, - 'SELECT * FROM users WHERE firstname = \'Chima\' AND age IN (22, 24, 25);', - ); - }); - - test('.whereNotIn', () { - final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isNotIn('age', [22, 24, 25]); - - expect( - query.statement, - 'SELECT * FROM users WHERE firstname = \'Chima\' AND age NOT IN (22, 24, 25);', - ); - }); - - test('.whereLike', () { - final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isLike('lastname', 'hello%'); - - expect( - query.statement, - 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname LIKE \'hello%\';', - ); - }); - - test('.whereNotLike', () { - final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isNotLike('lastname', 'hello%'); - - expect( - query.statement, - 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname NOT LIKE \'hello%\';', - ); - }); - - test('.whereBetween', () { - final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isBetween('lastname', [22, 50]); - - expect( - query.statement, - 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname BETWEEN 22 AND 50;', - ); - }); - - test('.whereNotBetween', () { - final query = - UserQuery.driver(driver).where('firstname', '=', 'Chima').isNotBetween('lastname', [22.34, 50]); - - expect( - query.statement, - 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname NOT BETWEEN 22.34 AND 50.0;', - ); - }); - }); - - test('with orderBy', () { - final query = UserQuery.driver(driver).where('name', '=', 'Chima').orderByDesc('names').orderByAsc('ages'); - - expect(query.statement, 'SELECT * FROM users WHERE name = \'Chima\' ORDER BY names DESC, ages ASC;'); - }); - }); - - group('when handwritten operator', () { - test('should error if unknown operator', () { - expect(() => UserQuery.driver(driver).where('age', 'foo-bar', '23').statement, - throwsA(isA().having((p0) => p0.message, '', 'Condition foo-bar is not known'))); - }); - - test('=', () { - final query = UserQuery.driver(driver).where('firstname', '=', 'Chima'); - - expect( - query.statement, - 'SELECT * FROM users WHERE firstname = \'Chima\';', - ); - }); - - test('!=', () { - final query = UserQuery.driver(driver).where('firstname', '!=', 'Chima'); - - expect(query.statement, 'SELECT * FROM users WHERE firstname != \'Chima\';'); - }); - - test('>', () { - final query = UserQuery.driver(driver).where('age', '>', 23); - - expect(query.statement, 'SELECT * FROM users WHERE age > 23;'); - }); - - test('<', () { - final query = UserQuery.driver(driver).where('age', '<', 23); - - expect(query.statement, 'SELECT * FROM users WHERE age < 23;'); - }); - - test('>=', () { - final query = UserQuery.driver(driver).where('age', '>=', 223); - - expect(query.statement, 'SELECT * FROM users WHERE age >= 223;'); - }); - - test('<=', () { - final query = UserQuery.driver(driver).where('age', '<=', 34.3); - - expect(query.statement, 'SELECT * FROM users WHERE age <= 34.3;'); - }); - - test('in', () { - final query = UserQuery.driver(driver).where('places', 'in', ['Accra', 'Tema']); - - expect( - query.statement, - 'SELECT * FROM users WHERE places IN (\'Accra\', \'Tema\');', - ); - }); - - test('not in', () { - final query = UserQuery.driver(driver).where('places', 'not in', ['Accra', 'Tema']); - - expect( - query.statement, - 'SELECT * FROM users WHERE places NOT IN (\'Accra\', \'Tema\');', - ); - }); - - test('null', () { - final query = UserQuery.driver(driver).where('places', 'null'); - - expect( - query.statement, - 'SELECT * FROM users WHERE places IS NULL;', - ); - }); - - test('not null', () { - final query = UserQuery.driver(driver).where('places', 'not null'); - - expect( - query.statement, - 'SELECT * FROM users WHERE places IS NOT NULL;', - ); - }); - - test('like', () { - final query = UserQuery.driver(driver).where('places', 'like', "MerryC"); - - expect( - query.statement, - 'SELECT * FROM users WHERE places LIKE \'MerryC\';', - ); - }); - - test('not like', () { - final query = UserQuery.driver(driver).where('places', 'not like', "MerryC"); - - expect( - query.statement, - 'SELECT * FROM users WHERE places NOT LIKE \'MerryC\';', - ); - }); - - test('between', () { - final query = UserQuery.driver(driver).where('age', 'between', [22, 30]); - - expect( - query.statement, - 'SELECT * FROM users WHERE age BETWEEN 22 AND 30;', - ); - }); - - test('not between', () { - final query = UserQuery.driver(driver).where('age', 'not between', [22, 30]); - - expect( - query.statement, - 'SELECT * FROM users WHERE age NOT BETWEEN 22 AND 30;', - ); - }); - }); - - group('when .whereIn', () { - test('of level 1', () { - final query = UserQuery.driver(driver).isIn('firstname', ['Accra', 'Tamale']); - - expect( - query.statement, - 'SELECT * FROM users WHERE firstname IN (\'Accra\', \'Tamale\');', - ); - }); - - test('of level 2', () { - final query = UserQuery.driver(driver).isIn('places', ['Accra', 'Tamale']).where('lastname', '=', 'Precious'); - - expect( - query.statement, - 'SELECT * FROM users WHERE places IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\';', - ); - }); - - test('of level 3', () { - final query = UserQuery.driver(driver) - .isIn('places', ['Accra', 'Tamale']) - .where('lastname', '=', 'Precious') - .where('names', 'like', 'Hello%'); - - expect( - query.statement, - 'SELECT * FROM users WHERE places IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\' AND names LIKE \'Hello%\';', - ); - }); - - test('of level 4', () { - final query = UserQuery.driver(driver) - .isIn('places', ['Accra', 'Tamale']) - .where('lastname', '=', 'Precious') - .where('names', 'like', 'Hello%') - .orWhere('age', 'in', [23, 34, 55]); - - expect( - query.statement, - 'SELECT * FROM users WHERE (places IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\' AND names LIKE \'Hello%\') OR age IN (23, 34, 55);', - ); - }); - }); - - group('when .whereNotIn', () { - test('of level 1', () { - final query = UserQuery.driver(driver).isNotIn('firstname', ['Accra', 'Tamale']); - - expect( - query.statement, - 'SELECT * FROM users WHERE firstname NOT IN (\'Accra\', \'Tamale\');', - ); - }); - - test('of level 2', () { - final query = - UserQuery.driver(driver).isNotIn('places', ['Accra', 'Tamale']).where('lastname', '=', 'Precious'); - - expect( - query.statement, - 'SELECT * FROM users WHERE places NOT IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\';', - ); - }); - - test('of level 3', () { - final query = UserQuery.driver(driver) - .isNotIn('places', ['Accra', 'Tamale']) - .where('lastname', '=', 'Precious') - .isNotNull('names'); - - expect( - query.statement, - 'SELECT * FROM users WHERE places NOT IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\' AND names IS NOT NULL;', - ); - }); - - test('of level 4', () { - final query = UserQuery.driver(driver) - .isNotIn('places', ['Accra', 'Tamale']) - .where('lastname', '=', 'Precious') - .where('names', 'like', 'Hello%') - .isBetween('age', [23, 34]); - - expect( - query.statement, - 'SELECT * FROM users WHERE places NOT IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\' AND names LIKE \'Hello%\' AND age BETWEEN 23 AND 34;', - ); - }); - }); - - group('when .whereBetween', () { - test('should error if not supplied List with length 2', () { - expect( - () => UserQuery.driver(driver).isBetween('age', [22]).statement, - throwsA(isA() - .having((p0) => p0.message, '', 'BETWEEN requires a List with length 2 (val1, val2)'))); - }); - - test('of level 1', () { - final query = UserQuery.driver(driver).isBetween('age', [22, 70]); - - expect( - query.statement, - 'SELECT * FROM users WHERE age BETWEEN 22 AND 70;', - ); - }); - - test('of level 2', () { - final query = - UserQuery.driver(driver).isBetween('places', ['Accra', 'Tamale']).where('lastname', 'between', [2, 100]); - - expect( - query.statement, - 'SELECT * FROM users WHERE places BETWEEN \'Accra\' AND \'Tamale\' AND lastname BETWEEN 2 AND 100;', - ); - }); - - test('of level 3', () { - final query = UserQuery.driver(driver) - .isIn('places', ['Accra', 'Tamale']).isBetween('lastname', [22, 48]).where('names', 'like', 'Hello%'); - - expect( - query.statement, - 'SELECT * FROM users WHERE places IN (\'Accra\', \'Tamale\') AND lastname BETWEEN 22 AND 48 AND names LIKE \'Hello%\';', - ); - }); - - test('of level 4', () { - final query = UserQuery.driver(driver) - .isIn('places', ['Accra', 'Tamale']) - .where('lastname', '=', 'Precious') - .orWhere('age', 'in', [23, 34, 55]) - .isBetween('dates', ['2015-01-01', '2016-12-01']); - - expect( - query.statement, - 'SELECT * FROM users WHERE (places IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\') OR (age IN (23, 34, 55) AND dates BETWEEN \'2015-01-01\' AND \'2016-12-01\');', - ); - }); - }); - - group('when .whereNotBetween', () { - test('should error if not supplied List with length 2', () { - expect( - () => UserQuery.driver(driver).isNotBetween('age', [22]).statement, - throwsA(isA() - .having((p0) => p0.message, '', 'NOT_BETWEEN requires a List with length 2 (val1, val2)'))); - }); - - test('of level 1', () { - final query = UserQuery.driver(driver).isNotBetween('age', [22, 70]); - - expect( - query.statement, - 'SELECT * FROM users WHERE age NOT BETWEEN 22 AND 70;', - ); - }); - - test('of level 2', () { - final query = - UserQuery.driver(driver).isNotBetween('places', ['Accra', 'Tamale']).where('lastname', 'between', [2, 100]); - - expect( - query.statement, - 'SELECT * FROM users WHERE places NOT BETWEEN \'Accra\' AND \'Tamale\' AND lastname BETWEEN 2 AND 100;', - ); - }); - - test('of level 3', () { - final query = UserQuery.driver(driver) - .isIn('places', ['Accra', 'Tamale']).isNotBetween('lastname', [22, 48]).where('names', 'like', 'Hello%'); - - expect( - query.statement, - 'SELECT * FROM users WHERE places IN (\'Accra\', \'Tamale\') AND lastname NOT BETWEEN 22 AND 48 AND names LIKE \'Hello%\';', - ); - }); - - test('of level 4', () { - final query = UserQuery.driver(driver) - .isIn('places', ['Accra', 'Tamale']) - .where('lastname', '=', 'Precious') - .orWhere('age', 'in', [23, 34, 55]) - .isNotBetween('dates', ['2015-01-01', '2016-12-01']); - - expect( - query.statement, - 'SELECT * FROM users WHERE (places IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\') OR (age IN (23, 34, 55) AND dates NOT BETWEEN \'2015-01-01\' AND \'2016-12-01\');', - ); - }); - }); - - group('when .whereLike', () { - test('of level 1', () { - final query = UserQuery.driver(driver).isLike('firstname', 'Names%%'); - - expect( - query.statement, - 'SELECT * FROM users WHERE firstname LIKE \'Names%%\';', - ); - }); - - test('of level 2', () { - final query = UserQuery.driver(driver).isLike('places', 'Chima**').where('lastname', '=', 'Precious'); - - expect( - query.statement, - 'SELECT * FROM users WHERE places LIKE \'Chima**\' AND lastname = \'Precious\';', - ); - }); - - test('of level 3', () { - final query = UserQuery.driver(driver) - .isLike('places', 'Hello123') - .where('lastname', '=', 'Precious') - .where('names', 'like', 'Hello%'); - - expect( - query.statement, - 'SELECT * FROM users WHERE places LIKE \'Hello123\' AND lastname = \'Precious\' AND names LIKE \'Hello%\';', - ); - }); - - test('of level 4', () { - final query = UserQuery.driver(driver) - .isLike('places', 'Nems#') - .where('lastname', '=', 'Precious') - .where('names', 'like', 'Hello%') - .orWhere('age', 'between', [23, 34]); - - expect( - query.statement, - 'SELECT * FROM users WHERE (places LIKE \'Nems#\' AND lastname = \'Precious\' AND names LIKE \'Hello%\') OR age BETWEEN 23 AND 34;', - ); - }); - }); - - group('when .whereNotLike', () { - test('of level 1', () { - final query = UserQuery.driver(driver).isNotLike('firstname', 'Names%%'); - - expect( - query.statement, - 'SELECT * FROM users WHERE firstname NOT LIKE \'Names%%\';', - ); - }); - - test('of level 2', () { - final query = UserQuery.driver(driver).isNotLike('places', 'Chima**').isBetween('lastname', [12, 90]); - - expect( - query.statement, - 'SELECT * FROM users WHERE places NOT LIKE \'Chima**\' AND lastname BETWEEN 12 AND 90;', - ); - }); - - test('of level 3', () { - final query = UserQuery.driver(driver) - .isNotLike('places', 'Hello123') - .where('lastname', '=', 'Precious') - .where('names', 'like', 'Hello%'); - - expect( - query.statement, - 'SELECT * FROM users WHERE places NOT LIKE \'Hello123\' AND lastname = \'Precious\' AND names LIKE \'Hello%\';', - ); - }); - - test('of level 4', () { - final query = UserQuery.driver(driver) - .isNotLike('places', 'Nems#') - .where('lastname', '=', 'Precious') - .orWhere('names', 'not like', 'Hello%') - .orWhere('age', 'between', [23, 34]); - - expect( - query.statement, - 'SELECT * FROM users WHERE (places NOT LIKE \'Nems#\' AND lastname = \'Precious\') OR names NOT LIKE \'Hello%\' OR age BETWEEN 23 AND 34;', - ); - }); - }); - - group('when .whereNull', () { - test('of level 1', () { - final query = UserQuery.driver(driver).isNull('firstname'); - - expect(query.statement, 'SELECT * FROM users WHERE firstname IS NULL;'); - }); - - test('of level 2', () { - final query = UserQuery.driver(driver).isNull('places').where('lastname', '=', 'Precious'); - - expect( - query.statement, - 'SELECT * FROM users WHERE places IS NULL AND lastname = \'Precious\';', - ); - }); - - test('of level 3', () { - final query = UserQuery.driver(driver) - .isNull('places') - .where('lastname', '=', 'Precious') - .where('names', 'like', 'Hello%'); - - expect( - query.statement, - 'SELECT * FROM users WHERE places IS NULL AND lastname = \'Precious\' AND names LIKE \'Hello%\';', - ); - }); - - test('of level 4', () { - final query = UserQuery.driver(driver) - .isNull('places') - .where('lastname', '=', 'Precious') - .orWhere('names', 'null') - .orWhere('age', 'between', [23, 34]); - - expect( - query.statement, - 'SELECT * FROM users WHERE (places IS NULL AND lastname = \'Precious\') OR names IS NULL OR age BETWEEN 23 AND 34;', - ); - }); - }); - - group('when .whereNotNull', () { - test('of level 1', () { - final query = UserQuery.driver(driver).isNotNull('firstname'); - - expect( - query.statement, - 'SELECT * FROM users WHERE firstname IS NOT NULL;', - ); - }); - - test('of level 2', () { - final query = UserQuery.driver(driver).isNotNull('places').where('lastname', '=', 'Precious'); - - expect( - query.statement, - 'SELECT * FROM users WHERE places IS NOT NULL AND lastname = \'Precious\';', - ); - }); - - test('of level 3', () { - final query = UserQuery.driver(driver) - .isNotNull('places') - .where('lastname', '=', 'Precious') - .where('names', 'like', 'Hello%'); - - expect( - query.statement, - 'SELECT * FROM users WHERE places IS NOT NULL AND lastname = \'Precious\' AND names LIKE \'Hello%\';', - ); - }); - - test('of level 4', () { - final query = UserQuery.driver(driver) - .isNotNull('places') - .where('lastname', '=', 'Precious') - .orWhere('names', 'not null') - .orWhere('age', 'between', [23, 34]); - - expect( - query.statement, - 'SELECT * FROM users WHERE (places IS NOT NULL AND lastname = \'Precious\') OR names IS NOT NULL OR age BETWEEN 23 AND 34;', - ); - }); - }); - - test('when .whereFunc', () { - final query = UserQuery.driver(driver) - .where('name', '=', 'John') - .whereFunc((query) => query.where('votes', '>', 100).orWhere('title', '=', 'Admin')); - - expect( - query.statement, - 'SELECT * FROM users WHERE name = \'John\' AND (votes > 100 OR title = \'Admin\');', - ); - }); - - group('when .orWhereFunc', () { - test('of level 1', () { - final query = UserQuery.driver(driver) - .where('votes', '>', 100) - .orWhereFunc((query) => query.where('name', '=', 'Abigail').where('votes', '>', 50)); - - expect( - query.statement, - 'SELECT * FROM users WHERE votes > 100 OR (name = \'Abigail\' AND votes > 50);', - ); - }); - - test('of level 2', () { - final query = UserQuery.driver(driver) - .where('votes', '>', 100) - .orWhereFunc((query) => query.where('name', '=', 'Abigail').where('votes', '>', 50)) - .orWhereFunc((query) => query.where('price', '=', 'GHC200').where('votes', 'not null')); - - expect( - query.statement, - 'SELECT * FROM users WHERE votes > 100 OR (name = \'Abigail\' AND votes > 50) OR (price = \'GHC200\' AND votes IS NOT NULL);', - ); - }); - - test('or level 3', () { - var query = UserQuery.driver(driver) - .where('votes', '>', 100) - .orWhereFunc((query) => query.where('name', '=', 'Abigail').where('votes', '>', 50)) - .where('name', '=', 22) - .orWhereFunc((query) => query.where('name', '=', 'Abigail')); - - expect( - query.statement, - 'SELECT * FROM users WHERE votes > 100 OR (name = \'Abigail\' AND votes > 50) AND name = 22 OR name = \'Abigail\';', - ); - }); - }); - - group('.whereFunc or .orWhereFunc', () { - group('when used at start of query', () { - test('when .orWhereFunc', () { - expect( - () => UserQuery.driver(driver).orWhereFunc((query) => query.where('name', '=', 'Abigail')), - throwsStateError, - ); - }); - - test('when .whereFunc', () { - var query = UserQuery.driver(driver).whereFunc((query) => query.where('name', '=', 'Abigail')); - - expect( - query.statement, - 'SELECT * FROM users WHERE name = \'Abigail\';', - ); - }); - }); - - test('when used together', () { - var query = UserQuery.driver(driver) - .whereFunc((query) => query.where('name', '=', 'Abigail')) - .orWhereFunc((query) => query.where('age', '<', 24).where('names', 'not null')); - - expect( - query.statement, - 'SELECT * FROM users WHERE name = \'Abigail\' OR (age < 24 AND names IS NOT NULL);', - ); - }); - - group('when nested crazy', () { - test('with level 1', () { - final query = UserQuery.driver(driver).where('name', '=', 'Chima').orWhereFunc( - (query) => query - .where('biscuit', '=', 'hello-world') - .orWhere('car_type', '=', 'lamborgini server') - .where('image', 'like', 'Image&&'), - ); - - expect( - query.statement, - "SELECT * FROM users WHERE name = 'Chima' OR (biscuit = 'hello-world' OR (car_type = 'lamborgini server' AND image LIKE 'Image&&'));", - ); - }); - - test('with level 2', () { - final query = UserQuery.driver(driver) - .where('name', '=', 'Chima') - .orWhereFunc( - (query) => query - .where('biscuit', '=', 'hello-world') - .orWhere('car_type', '=', 'lamborgini server') - .where('image', 'like', 'Image&&'), - ) - .whereFunc((query) => query - .isIn('fruits', ['oranges', 'apples']) - .isBetween('price', [20, 100]) - .equal('status', 'available') - .isLike('stores', 'Accra, %%')); - - final sB = StringBuffer(); - sB.write("SELECT * FROM users WHERE name = 'Chima' "); - sB.write("OR (biscuit = 'hello-world' OR (car_type = 'lamborgini server' AND image LIKE 'Image&&')) "); - sB.write( - "AND (fruits IN ('oranges', 'apples') AND price BETWEEN 20 AND 100 AND status = 'available' AND stores LIKE 'Accra, %%');"); - - expect(query.statement, sB.toString()); - }); - - test('with level 3', () { - final query = UserQuery.driver(driver) - .where('name', '=', 'Chima') - .orWhereFunc( - (query) => query - .where('biscuit', '=', 'hello-world') - .orWhere('car_type', '=', 'lamborgini server') - .where('image', 'like', 'Image&&'), - ) - .whereFunc((query) => query - .isIn('fruits', ['oranges', 'apples']) - .isBetween('price', [20, 100]) - .equal('status', 'available') - .isLike('stores', 'Accra, %%')) - .orWhereFunc((query) => query.where('languages', 'in', ['python', 'cobra']).orWhereFunc((query) => query - .where('job_status', '=', 'available') - .where('location', '=', 'Accra') - .isNotBetween('salary', [8000, 16000]))); - - final sB = StringBuffer(); - sB.write("SELECT * FROM users WHERE name = 'Chima' "); - sB.write("OR (biscuit = 'hello-world' OR (car_type = 'lamborgini server' AND image LIKE 'Image&&')) "); - sB.write( - "AND (fruits IN ('oranges', 'apples') AND price BETWEEN 20 AND 100 AND status = 'available' AND stores LIKE 'Accra, %%') "); - sB.write( - "OR (languages IN ('python', 'cobra') OR (job_status = 'available' AND location = 'Accra' AND salary NOT BETWEEN 8000 AND 16000));"); - - expect(query.statement, sB.toString()); - }); - }); - }); - }); + // test('when query with multiple orderBy', () { + // final query = UserQuery.driver(driver).orderByDesc('names').orderByAsc('ages'); + + // expect(query.statement, 'SELECT * FROM users ORDER BY names DESC, ages ASC;'); + // }); + + // // test('when update', () { + // // final query = UserQuery.driver(driver).update( + // // where: (where) => where.where('name', '=', 'Chima'), + // // values: {'firstname': 'Chima', 'lastname': 'Precious'}, + // // ); + + // // expect(query.statement, + // // 'UPDATE users SET firstname = ?, lastname = ? WHERE name = \'Chima\';'); + // // }); + + // // test('when delete', () { + // // final query = UserQuery + // // .driver(driver) + // // .delete((where) => where.where('name', '=', 'Chima')); + + // // expect(query.statement, 'DELETE FROM users WHERE name = \'Chima\';'); + // // }); + + // // group('when .where', () { + // // test('of level 1', () { + // // final query = UserQuery.driver(driver).where('firstname', '=', 'Chima'); + + // // expect(query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\';'); + // // }); + + // // test('of level 2', () { + // // final query = UserQuery.driver(driver).whereFirstname('Chima').where('lastname', '=', 'Precious'); + + // // expect(query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname = \'Precious\';'); + // // }); + + // // test('of level 3', () { + // // final query$ = UserQuery.driver(driver).where((b) => b.equal('hello', 'Chima')).; + + // // final query = + // // UserQuery.driver(driver).whereFirstname('Chima').equal('lastname', 'Precious').where('age', '=', 22); + + // // expect(query.statement, + // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname = \'Precious\' AND age = 22;'); + // // }); + + // // group('chained with `.orWhere`', () { + // // test('of level 1', () { + // // final query = UserQuery.driver(driver).whereFirstname('Chima').orWhere('age', '=', 203); + + // // expect(query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\' OR age = 203;'); + // // }); + + // // test('of level 2', () { + // // final query = + // // UserQuery.driver(driver).whereFirstname('Chima').orWhere('age', '=', 203).where('city', '!=', 'Accra'); + + // // expect( + // // query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\' OR (age = 203 AND city != \'Accra\');'); + // // }); + + // // test('of level 3', () { + // // final query = + // // UserQuery.driver(driver).where('votes', '>', 100).orWhere('name', '=', 'Abigail').where('votes', '>', 50); + + // // expect(query.statement, 'SELECT * FROM users WHERE votes > 100 OR (name = \'Abigail\' AND votes > 50);'); + // // }); + + // // test('of level 4', () { + // // final query = UserQuery.driver(driver) + // // .where('firstname', '=', 'Chima') + // // .orWhere('age', '=', 203) + // // .orWhere('city', '!=', 'Accra') + // // .where('name', 'like', 'Chima%'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE firstname = \'Chima\' OR age = 203 OR (city != \'Accra\' AND name LIKE \'Chima%\');', + // // ); + // // }); + + // // test('of level 5', () { + // // final query = UserQuery.driver(driver) + // // .where('firstname', '=', 'Chima') + // // .orWhere('age', '=', 203) + // // .orWhere('city', '!=', 'Accra') + // // .where('name', 'like', 'Chima%') + // // .where('sizes', 'between', [12, 23]); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE firstname = \'Chima\' OR age = 203 OR (city != \'Accra\' AND name LIKE \'Chima%\' AND sizes BETWEEN 12 AND 23);', + // // ); + // // }); + // // }); + + // // group('chained with', () { + // // test('.whereNull', () { + // // final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isNull('age'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND age IS NULL;', + // // ); + // // }); + + // // test('.whereNotNull', () { + // // final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isNotNull('age'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND age IS NOT NULL;', + // // ); + // // }); + + // // test('.whereIn', () { + // // final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isIn('age', [22, 24, 25]); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND age IN (22, 24, 25);', + // // ); + // // }); + + // // test('.whereNotIn', () { + // // final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isNotIn('age', [22, 24, 25]); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND age NOT IN (22, 24, 25);', + // // ); + // // }); + + // // test('.whereLike', () { + // // final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isLike('lastname', 'hello%'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname LIKE \'hello%\';', + // // ); + // // }); + + // // test('.whereNotLike', () { + // // final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isNotLike('lastname', 'hello%'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname NOT LIKE \'hello%\';', + // // ); + // // }); + + // // test('.whereBetween', () { + // // final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isBetween('lastname', [22, 50]); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname BETWEEN 22 AND 50;', + // // ); + // // }); + + // // test('.whereNotBetween', () { + // // final query = + // // UserQuery.driver(driver).where('firstname', '=', 'Chima').isNotBetween('lastname', [22.34, 50]); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname NOT BETWEEN 22.34 AND 50.0;', + // // ); + // // }); + // // }); + + // // test('with orderBy', () { + // // final query = UserQuery.driver(driver).where('name', '=', 'Chima').orderByDesc('names').orderByAsc('ages'); + + // // expect(query.statement, 'SELECT * FROM users WHERE name = \'Chima\' ORDER BY names DESC, ages ASC;'); + // // }); + // // }); + + // // group('when .whereIn', () { + // // test('of level 1', () { + // // final query = UserQuery.driver(driver).isIn('firstname', ['Accra', 'Tamale']); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE firstname IN (\'Accra\', \'Tamale\');', + // // ); + // // }); + + // // test('of level 2', () { + // // final query = UserQuery.driver(driver).isIn('places', ['Accra', 'Tamale']).where('lastname', '=', 'Precious'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\';', + // // ); + // // }); + + // // test('of level 3', () { + // // final query = UserQuery.driver(driver) + // // .isIn('places', ['Accra', 'Tamale']) + // // .where('lastname', '=', 'Precious') + // // .where('names', 'like', 'Hello%'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\' AND names LIKE \'Hello%\';', + // // ); + // // }); + + // // test('of level 4', () { + // // final query = UserQuery.driver(driver) + // // .isIn('places', ['Accra', 'Tamale']) + // // .where('lastname', '=', 'Precious') + // // .where('names', 'like', 'Hello%') + // // .orWhere('age', 'in', [23, 34, 55]); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE (places IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\' AND names LIKE \'Hello%\') OR age IN (23, 34, 55);', + // // ); + // // }); + // // }); + + // // group('when .whereNotIn', () { + // // test('of level 1', () { + // // final query = UserQuery.driver(driver).isNotIn('firstname', ['Accra', 'Tamale']); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE firstname NOT IN (\'Accra\', \'Tamale\');', + // // ); + // // }); + + // // test('of level 2', () { + // // final query = + // // UserQuery.driver(driver).isNotIn('places', ['Accra', 'Tamale']).where('lastname', '=', 'Precious'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places NOT IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\';', + // // ); + // // }); + + // // test('of level 3', () { + // // final query = UserQuery.driver(driver) + // // .isNotIn('places', ['Accra', 'Tamale']) + // // .where('lastname', '=', 'Precious') + // // .isNotNull('names'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places NOT IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\' AND names IS NOT NULL;', + // // ); + // // }); + + // // test('of level 4', () { + // // final query = UserQuery.driver(driver) + // // .isNotIn('places', ['Accra', 'Tamale']) + // // .where('lastname', '=', 'Precious') + // // .where('names', 'like', 'Hello%') + // // .isBetween('age', [23, 34]); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places NOT IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\' AND names LIKE \'Hello%\' AND age BETWEEN 23 AND 34;', + // // ); + // // }); + // // }); + + // // group('when .whereBetween', () { + // // test('should error if not supplied List with length 2', () { + // // expect( + // // () => UserQuery.driver(driver).isBetween('age', [22]).statement, + // // throwsA(isA() + // // .having((p0) => p0.message, '', 'BETWEEN requires a List with length 2 (val1, val2)'))); + // // }); + + // // test('of level 1', () { + // // final query = UserQuery.driver(driver).isBetween('age', [22, 70]); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE age BETWEEN 22 AND 70;', + // // ); + // // }); + + // // test('of level 2', () { + // // final query = + // // UserQuery.driver(driver).isBetween('places', ['Accra', 'Tamale']).where('lastname', 'between', [2, 100]); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places BETWEEN \'Accra\' AND \'Tamale\' AND lastname BETWEEN 2 AND 100;', + // // ); + // // }); + + // // test('of level 3', () { + // // final query = UserQuery.driver(driver) + // // .isIn('places', ['Accra', 'Tamale']).isBetween('lastname', [22, 48]).where('names', 'like', 'Hello%'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places IN (\'Accra\', \'Tamale\') AND lastname BETWEEN 22 AND 48 AND names LIKE \'Hello%\';', + // // ); + // // }); + + // // test('of level 4', () { + // // final query = UserQuery.driver(driver) + // // .isIn('places', ['Accra', 'Tamale']) + // // .where('lastname', '=', 'Precious') + // // .orWhere('age', 'in', [23, 34, 55]) + // // .isBetween('dates', ['2015-01-01', '2016-12-01']); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE (places IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\') OR (age IN (23, 34, 55) AND dates BETWEEN \'2015-01-01\' AND \'2016-12-01\');', + // // ); + // // }); + // // }); + + // // group('when .whereNotBetween', () { + // // test('should error if not supplied List with length 2', () { + // // expect( + // // () => UserQuery.driver(driver).isNotBetween('age', [22]).statement, + // // throwsA(isA() + // // .having((p0) => p0.message, '', 'NOT_BETWEEN requires a List with length 2 (val1, val2)'))); + // // }); + + // // test('of level 1', () { + // // final query = UserQuery.driver(driver).isNotBetween('age', [22, 70]); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE age NOT BETWEEN 22 AND 70;', + // // ); + // // }); + + // // test('of level 2', () { + // // final query = + // // UserQuery.driver(driver).isNotBetween('places', ['Accra', 'Tamale']).where('lastname', 'between', [2, 100]); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places NOT BETWEEN \'Accra\' AND \'Tamale\' AND lastname BETWEEN 2 AND 100;', + // // ); + // // }); + + // // test('of level 3', () { + // // final query = UserQuery.driver(driver) + // // .isIn('places', ['Accra', 'Tamale']).isNotBetween('lastname', [22, 48]).where('names', 'like', 'Hello%'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places IN (\'Accra\', \'Tamale\') AND lastname NOT BETWEEN 22 AND 48 AND names LIKE \'Hello%\';', + // // ); + // // }); + + // // test('of level 4', () { + // // final query = UserQuery.driver(driver) + // // .isIn('places', ['Accra', 'Tamale']) + // // .where('lastname', '=', 'Precious') + // // .orWhere('age', 'in', [23, 34, 55]) + // // .isNotBetween('dates', ['2015-01-01', '2016-12-01']); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE (places IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\') OR (age IN (23, 34, 55) AND dates NOT BETWEEN \'2015-01-01\' AND \'2016-12-01\');', + // // ); + // // }); + // // }); + + // // group('when .whereLike', () { + // // test('of level 1', () { + // // final query = UserQuery.driver(driver).isLike('firstname', 'Names%%'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE firstname LIKE \'Names%%\';', + // // ); + // // }); + + // // test('of level 2', () { + // // final query = UserQuery.driver(driver).isLike('places', 'Chima**').where('lastname', '=', 'Precious'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places LIKE \'Chima**\' AND lastname = \'Precious\';', + // // ); + // // }); + + // // test('of level 3', () { + // // final query = UserQuery.driver(driver) + // // .isLike('places', 'Hello123') + // // .where('lastname', '=', 'Precious') + // // .where('names', 'like', 'Hello%'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places LIKE \'Hello123\' AND lastname = \'Precious\' AND names LIKE \'Hello%\';', + // // ); + // // }); + + // // test('of level 4', () { + // // final query = UserQuery.driver(driver) + // // .isLike('places', 'Nems#') + // // .where('lastname', '=', 'Precious') + // // .where('names', 'like', 'Hello%') + // // .orWhere('age', 'between', [23, 34]); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE (places LIKE \'Nems#\' AND lastname = \'Precious\' AND names LIKE \'Hello%\') OR age BETWEEN 23 AND 34;', + // // ); + // // }); + // // }); + + // // group('when .whereNotLike', () { + // // test('of level 1', () { + // // final query = UserQuery.driver(driver).isNotLike('firstname', 'Names%%'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE firstname NOT LIKE \'Names%%\';', + // // ); + // // }); + + // // test('of level 2', () { + // // final query = UserQuery.driver(driver).isNotLike('places', 'Chima**').isBetween('lastname', [12, 90]); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places NOT LIKE \'Chima**\' AND lastname BETWEEN 12 AND 90;', + // // ); + // // }); + + // // test('of level 3', () { + // // final query = UserQuery.driver(driver) + // // .isNotLike('places', 'Hello123') + // // .where('lastname', '=', 'Precious') + // // .where('names', 'like', 'Hello%'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places NOT LIKE \'Hello123\' AND lastname = \'Precious\' AND names LIKE \'Hello%\';', + // // ); + // // }); + + // // test('of level 4', () { + // // final query = UserQuery.driver(driver) + // // .isNotLike('places', 'Nems#') + // // .where('lastname', '=', 'Precious') + // // .orWhere('names', 'not like', 'Hello%') + // // .orWhere('age', 'between', [23, 34]); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE (places NOT LIKE \'Nems#\' AND lastname = \'Precious\') OR names NOT LIKE \'Hello%\' OR age BETWEEN 23 AND 34;', + // // ); + // // }); + // // }); + + // // group('when .whereNull', () { + // // test('of level 1', () { + // // final query = UserQuery.driver(driver).isNull('firstname'); + + // // expect(query.statement, 'SELECT * FROM users WHERE firstname IS NULL;'); + // // }); + + // // test('of level 2', () { + // // final query = UserQuery.driver(driver).isNull('places').where('lastname', '=', 'Precious'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places IS NULL AND lastname = \'Precious\';', + // // ); + // // }); + + // // test('of level 3', () { + // // final query = UserQuery.driver(driver) + // // .isNull('places') + // // .where('lastname', '=', 'Precious') + // // .where('names', 'like', 'Hello%'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places IS NULL AND lastname = \'Precious\' AND names LIKE \'Hello%\';', + // // ); + // // }); + + // // test('of level 4', () { + // // final query = UserQuery.driver(driver) + // // .isNull('places') + // // .where('lastname', '=', 'Precious') + // // .orWhere('names', 'null') + // // .orWhere('age', 'between', [23, 34]); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE (places IS NULL AND lastname = \'Precious\') OR names IS NULL OR age BETWEEN 23 AND 34;', + // // ); + // // }); + // // }); + + // // group('when .whereNotNull', () { + // // test('of level 1', () { + // // final query = UserQuery.driver(driver).isNotNull('firstname'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE firstname IS NOT NULL;', + // // ); + // // }); + + // // test('of level 2', () { + // // final query = UserQuery.driver(driver).isNotNull('places').where('lastname', '=', 'Precious'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places IS NOT NULL AND lastname = \'Precious\';', + // // ); + // // }); + + // // test('of level 3', () { + // // final query = UserQuery.driver(driver) + // // .isNotNull('places') + // // .where('lastname', '=', 'Precious') + // // .where('names', 'like', 'Hello%'); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE places IS NOT NULL AND lastname = \'Precious\' AND names LIKE \'Hello%\';', + // // ); + // // }); + + // // test('of level 4', () { + // // final query = UserQuery.driver(driver) + // // .isNotNull('places') + // // .where('lastname', '=', 'Precious') + // // .orWhere('names', 'not null') + // // .orWhere('age', 'between', [23, 34]); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE (places IS NOT NULL AND lastname = \'Precious\') OR names IS NOT NULL OR age BETWEEN 23 AND 34;', + // // ); + // // }); + // // }); + + // // test('when .whereFunc', () { + // // final query = UserQuery.driver(driver) + // // .where('name', '=', 'John') + // // .whereFunc((query) => query.where('votes', '>', 100).orWhere('title', '=', 'Admin')); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE name = \'John\' AND (votes > 100 OR title = \'Admin\');', + // // ); + // // }); + + // // group('when .orWhereFunc', () { + // // test('of level 1', () { + // // final query = UserQuery.driver(driver) + // // .where('votes', '>', 100) + // // .orWhereFunc((query) => query.where('name', '=', 'Abigail').where('votes', '>', 50)); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE votes > 100 OR (name = \'Abigail\' AND votes > 50);', + // // ); + // // }); + + // // test('of level 2', () { + // // final query = UserQuery.driver(driver) + // // .where('votes', '>', 100) + // // .orWhereFunc((query) => query.where('name', '=', 'Abigail').where('votes', '>', 50)) + // // .orWhereFunc((query) => query.where('price', '=', 'GHC200').where('votes', 'not null')); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE votes > 100 OR (name = \'Abigail\' AND votes > 50) OR (price = \'GHC200\' AND votes IS NOT NULL);', + // // ); + // // }); + + // // test('or level 3', () { + // // var query = UserQuery.driver(driver) + // // .where('votes', '>', 100) + // // .orWhereFunc((query) => query.where('name', '=', 'Abigail').where('votes', '>', 50)) + // // .where('name', '=', 22) + // // .orWhereFunc((query) => query.where('name', '=', 'Abigail')); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE votes > 100 OR (name = \'Abigail\' AND votes > 50) AND name = 22 OR name = \'Abigail\';', + // // ); + // // }); + // // }); + + // // group('.whereFunc or .orWhereFunc', () { + // // group('when used at start of query', () { + // // test('when .orWhereFunc', () { + // // expect( + // // () => UserQuery.driver(driver).orWhereFunc((query) => query.where('name', '=', 'Abigail')), + // // throwsStateError, + // // ); + // // }); + + // // test('when .whereFunc', () { + // // var query = UserQuery.driver(driver).whereFunc((query) => query.where('name', '=', 'Abigail')); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE name = \'Abigail\';', + // // ); + // // }); + // // }); + + // // test('when used together', () { + // // var query = UserQuery.driver(driver) + // // .whereFunc((query) => query.where('name', '=', 'Abigail')) + // // .orWhereFunc((query) => query.where('age', '<', 24).where('names', 'not null')); + + // // expect( + // // query.statement, + // // 'SELECT * FROM users WHERE name = \'Abigail\' OR (age < 24 AND names IS NOT NULL);', + // // ); + // // }); + + // // group('when nested crazy', () { + // // test('with level 1', () { + // // final query = UserQuery.driver(driver).where('name', '=', 'Chima').orWhereFunc( + // // (query) => query + // // .where('biscuit', '=', 'hello-world') + // // .orWhere('car_type', '=', 'lamborgini server') + // // .where('image', 'like', 'Image&&'), + // // ); + + // // expect( + // // query.statement, + // // "SELECT * FROM users WHERE name = 'Chima' OR (biscuit = 'hello-world' OR (car_type = 'lamborgini server' AND image LIKE 'Image&&'));", + // // ); + // // }); + + // // test('with level 2', () { + // // final query = UserQuery.driver(driver) + // // .where('name', '=', 'Chima') + // // .orWhereFunc( + // // (query) => query + // // .where('biscuit', '=', 'hello-world') + // // .orWhere('car_type', '=', 'lamborgini server') + // // .where('image', 'like', 'Image&&'), + // // ) + // // .whereFunc((query) => query + // // .isIn('fruits', ['oranges', 'apples']) + // // .isBetween('price', [20, 100]) + // // .equal('status', 'available') + // // .isLike('stores', 'Accra, %%')); + + // // final sB = StringBuffer(); + // // sB.write("SELECT * FROM users WHERE name = 'Chima' "); + // // sB.write("OR (biscuit = 'hello-world' OR (car_type = 'lamborgini server' AND image LIKE 'Image&&')) "); + // // sB.write( + // // "AND (fruits IN ('oranges', 'apples') AND price BETWEEN 20 AND 100 AND status = 'available' AND stores LIKE 'Accra, %%');"); + + // // expect(query.statement, sB.toString()); + // // }); + + // // test('with level 3', () { + // // final query = UserQuery.driver(driver) + // // .where('name', '=', 'Chima') + // // .orWhereFunc( + // // (query) => query + // // .where('biscuit', '=', 'hello-world') + // // .orWhere('car_type', '=', 'lamborgini server') + // // .where('image', 'like', 'Image&&'), + // // ) + // // .whereFunc((query) => query + // // .isIn('fruits', ['oranges', 'apples']) + // // .isBetween('price', [20, 100]) + // // .equal('status', 'available') + // // .isLike('stores', 'Accra, %%')) + // // .orWhereFunc((query) => query.where('languages', 'in', ['python', 'cobra']).orWhereFunc((query) => query + // // .where('job_status', '=', 'available') + // // .where('location', '=', 'Accra') + // // .isNotBetween('salary', [8000, 16000]))); + + // // final sB = StringBuffer(); + // // sB.write("SELECT * FROM users WHERE name = 'Chima' "); + // // sB.write("OR (biscuit = 'hello-world' OR (car_type = 'lamborgini server' AND image LIKE 'Image&&')) "); + // // sB.write( + // // "AND (fruits IN ('oranges', 'apples') AND price BETWEEN 20 AND 100 AND status = 'available' AND stores LIKE 'Accra, %%') "); + // // sB.write( + // // "OR (languages IN ('python', 'cobra') OR (job_status = 'available' AND location = 'Accra' AND salary NOT BETWEEN 8000 AND 16000));"); + + // // expect(query.statement, sB.toString()); + // // }); + // // }); + // // }); + // }); // group('SQLITE Table Blueprint', () { // // diff --git a/packages/orm/test/yaroorm_test.dart b/packages/orm/test/yaroorm_test.dart index fccead09..904d1c6f 100644 --- a/packages/orm/test/yaroorm_test.dart +++ b/packages/orm/test/yaroorm_test.dart @@ -92,7 +92,7 @@ void main() { test('should err when Query without driver', () async { late Object error; try { - await Query.table().all(); + await Query.table().findMany(); } catch (e) { error = e; } diff --git a/packages/orm_cli/lib/orm/_misc.dart b/packages/orm_cli/lib/orm/_misc.dart index c978349e..89610a1b 100644 --- a/packages/orm_cli/lib/orm/_misc.dart +++ b/packages/orm_cli/lib/orm/_misc.dart @@ -16,7 +16,7 @@ Future hasAlreadyMigratedScript( String scriptName, DatabaseDriver driver, ) async { - final result = await MigrationQuery.driver(driver).whereMigration(scriptName).findOne(); + final result = await MigrationQuery.driver(driver).findByMigration(scriptName); return result != null; } diff --git a/packages/orm_cli/lib/orm/commands/migrate_reset_command.dart b/packages/orm_cli/lib/orm/commands/migrate_reset_command.dart index a58b3066..5dfc6faa 100644 --- a/packages/orm_cli/lib/orm/commands/migrate_reset_command.dart +++ b/packages/orm_cli/lib/orm/commands/migrate_reset_command.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:collection/collection.dart'; +import 'package:yaroo_cli/src/migration.dart'; import 'package:yaroorm/yaroorm.dart'; import '../_misc.dart'; @@ -18,7 +19,9 @@ class MigrationResetCommand extends OrmCommand { Future execute(DatabaseDriver driver) async { await ensureMigrationsTableReady(driver); - final migrationsList = await MigrationQuery.driver(driver).orderByDesc('batch').all(); + final migrationsList = await MigrationQuery.driver(driver).findMany( + orderBy: [OrderMigrationEntityBy.batch(OrderDirection.desc)], + ); if (migrationsList.isEmpty) { print('𐄂 skipped: reason: no migrations to reset'); return; diff --git a/packages/orm_cli/lib/orm/commands/migrate_rollback_command.dart b/packages/orm_cli/lib/orm/commands/migrate_rollback_command.dart index 543cbd32..3c76cd14 100644 --- a/packages/orm_cli/lib/orm/commands/migrate_rollback_command.dart +++ b/packages/orm_cli/lib/orm/commands/migrate_rollback_command.dart @@ -20,7 +20,11 @@ class MigrationRollbackCommand extends OrmCommand { final lastBatchNumber = await getLastBatchNumber(driver, migrationTableName); - final entries = await MigrationEntityQuery.driver(driver).equal('batch', lastBatchNumber).findMany(); + final entries = await MigrationEntityQuery.driver(driver) + .where( + (migration) => migration.batch(lastBatchNumber), + ) + .findMany(); /// rollbacks start from the last class listed in the migrations list final migrationTask = migrationDefinitions @@ -56,7 +60,11 @@ Future processRollbacks( await transactor.execute(e.toScript(driver.blueprint)); } - await MigrationQuery.driver(transactor).whereId(rollback.entry.id).delete(); + await MigrationQuery.driver(transactor) + .where( + (migration) => migration.id(rollback.entry.id), + ) + .delete(); }); print('✔ rolled back: ${rollback.entry.migration}'); diff --git a/packages/orm_cli/lib/src/migration.g.dart b/packages/orm_cli/lib/src/migration.g.dart index 68358b0b..6570d8a0 100644 --- a/packages/orm_cli/lib/src/migration.g.dart +++ b/packages/orm_cli/lib/src/migration.g.dart @@ -10,7 +10,8 @@ part of 'migration.dart'; Query get MigrationEntityQuery => DB.query(); CreateSchema get MigrationEntitySchema => Schema.fromEntity(); -DBEntity get migration_entityTypeData => DBEntity( +DBEntity get migration_entityTypeData => + DBEntity( "migrations", timestampsEnabled: false, columns: [ @@ -40,13 +41,21 @@ class _$MigrationEntityEntityMirror extends EntityMirror { } } +class OrderMigrationEntityBy extends OrderBy { + const OrderMigrationEntityBy.migration(OrderDirection direction) + : super("migration", direction); + + const OrderMigrationEntityBy.batch(OrderDirection direction) + : super("batch", direction); +} + extension MigrationEntityQueryExtension on Query { - WhereClause whereId(int value) => equal("id", value); - WhereClause whereMigration(String value) => equal("migration", value); - WhereClause whereBatch(int value) => equal("batch", value); - Future findById(int value) => whereId(value).findOne(); - Future findByMigration(String value) => whereMigration(value).findOne(); - Future findByBatch(int value) => whereBatch(value).findOne(); + Future findById(int val) => + findOne(where: (q) => q.id(val)); + Future findByMigration(String val) => + findOne(where: (q) => q.migration(val)); + Future findByBatch(int val) => + findOne(where: (q) => q.batch(val)); Future create({ required String migration, required int batch, @@ -55,12 +64,20 @@ extension MigrationEntityQueryExtension on Query { } } -extension MigrationEntityUpdateQueryExtension on WhereClause { +extension MigrationEntityWhereBuilderExtension + on WhereClauseBuilder { + WhereClauseValue id(int value) => $equal("id", value); + WhereClauseValue migration(String value) => + $equal("migration", value); + WhereClauseValue batch(int value) => $equal("batch", value); +} + +extension MigrationEntityUpdateExtension on ReadQuery { Future update({ value migration = const NoValue(), value batch = const NoValue(), }) async { - await query.$update(where: (_) => this, values: { + await $query.$update(where: (_) => whereClause!, values: { if (migration is! NoValue) #migration: migration.val, if (batch is! NoValue) #batch: batch.val, }).execute(); From 418831f87ac653a6436f2f8f90ed00b9082162f5 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sat, 13 Apr 2024 16:17:43 +0000 Subject: [PATCH 41/50] tiny improvements --- packages/generator/lib/src/generator.dart | 111 ++++++++++++++-------- 1 file changed, 70 insertions(+), 41 deletions(-) diff --git a/packages/generator/lib/src/generator.dart b/packages/generator/lib/src/generator.dart index 43960b11..25def5e4 100644 --- a/packages/generator/lib/src/generator.dart +++ b/packages/generator/lib/src/generator.dart @@ -44,8 +44,6 @@ class YaroormGenerator extends GeneratorForAnnotation { return _implementClass(element, annotation); } - TypeChecker _typeChecker(Type type) => TypeChecker.fromRuntime(type); - FieldData? _getFieldAnnotationByType( List fields, Type type, @@ -97,16 +95,9 @@ class YaroormGenerator extends GeneratorForAnnotation { String generateCodeForField(FieldElement e) { final symbol = '#${e.name}'; - var columnName = e.name; + final columnName = _getFieldDbName(e); final meta = _typeChecker(entity.TableColumn).firstAnnotationOf(e, throwOnUnresolved: false); - ConstantReader? metaReader; - - if (meta != null) { - metaReader = ConstantReader(meta); - final customName = metaReader.peek('name')?.stringValue; - if (customName != null) columnName = customName; - } final requiredOpts = ''' "$columnName", @@ -115,6 +106,7 @@ class YaroormGenerator extends GeneratorForAnnotation { '''; if (meta != null) { + final metaReader = ConstantReader(meta); final isReferenceField = _typeChecker(entity.reference).isExactly(meta.type!.element!); if (isReferenceField) { @@ -192,6 +184,8 @@ class YaroormGenerator extends GeneratorForAnnotation { build: (args) => ${_generateConstructorCode(className, primaryConstructor)}, ${converters.isEmpty ? '' : 'converters: ${converters.map(processAnnotation).toList()},'})''', )), + + /// Generate Entity Mirror for Reflection Class( (b) => b ..name = '_\$${className}EntityMirror' @@ -204,30 +198,49 @@ class YaroormGenerator extends GeneratorForAnnotation { ..name = 'instance')), )) ..methods.addAll([ - Method((m) => m - ..name = 'get' - ..annotations.add(CodeExpression(Code('override'))) - ..requiredParameters.add(Parameter((p) => p - ..name = 'field' - ..type = refer('Symbol'))) - ..returns = refer('Object?') - ..body = Code(''' + Method( + (m) => m + ..name = 'get' + ..annotations.add(CodeExpression(Code('override'))) + ..requiredParameters.add(Parameter((p) => p + ..name = 'field' + ..type = refer('Symbol'))) + ..returns = refer('Object?') + ..body = Code(''' return switch(field) { ${fields.map((e) => ''' #${e.name} => instance.${e.name} ''').join(',')}, _ => throw Exception('Unknown property \$field'), }; -''')), +'''), + ), ]), ), + + /// Generate Typed OrderBy's + Class((b) => b + ..name = 'Order${className}By' + ..extend = refer('OrderBy<$className>') + ..constructors.addAll([ + ...normalFields, + if (createdAtField != null) createdAtField, + if (updatedAtField != null) updatedAtField, + ].map((field) => Constructor((c) => c + ..name = field.name + ..constant = true + ..lambda = true + ..initializers.add(Code('super("${_getFieldDbName(field)}", direction)')) + ..requiredParameters.add(Parameter((p) => p + ..type = refer('OrderDirection') + ..name = 'direction')))))), + + /// Generate Entity Create Extension Extension( (b) => b ..name = '${className}QueryExtension' ..on = refer('Query<$className>') ..methods.addAll([ - _generateFieldWhereClause(primaryKey.field, className), - ...normalFields.map((e) => _generateFieldWhereClause(e, className)), _generateGetByPropertyMethod(primaryKey.field, className), ...normalFields.map((e) => _generateGetByPropertyMethod(e, className)), Method( @@ -247,9 +260,20 @@ return switch(field) { ), ]), ), + + /// Generate Typed Entity WhereBuilder Extension Extension((b) => b - ..name = '${className}UpdateQueryExtension' - ..on = refer('WhereClause<$className>') + ..name = '${className}WhereBuilderExtension' + ..on = refer('WhereClauseBuilder<$className>') + ..methods.addAll([ + _generateFieldWhereClause(primaryKey.field, className), + ...normalFields.map((e) => _generateFieldWhereClause(e, className)), + ])), + + /// Generate Typed Entity Update Extension + Extension((b) => b + ..name = '${className}UpdateExtension' + ..on = refer('ReadQuery<$className>') ..methods.addAll([ Method( (m) => m @@ -266,8 +290,8 @@ return switch(field) { ..required = false, ), )) - ..body = Code('''await query.\$update( - where: (_) => this, + ..body = Code('''await \$query.\$update( + where: (_) => whereClause!, values: { ${normalFields.map((e) => 'if (${e.name} is! NoValue) #${e.name}: ${e.name}.val').join(',')}, }).execute();'''), @@ -275,7 +299,7 @@ return switch(field) { ])) ])); - final emitter = DartEmitter(useNullSafetySyntax: true); + final emitter = DartEmitter(useNullSafetySyntax: true, orderDirectives: true); return DartFormatter().format('${library.accept(emitter)}'); } @@ -306,42 +330,38 @@ return switch(field) { /// This generates WHERE-EQUAL Clauses for a field Method _generateFieldWhereClause(FieldElement field, String className) { - final meta = _typeChecker(entity.TableColumn).firstAnnotationOf(field, throwOnUnresolved: false); - final ConstantReader? reader = meta == null ? null : ConstantReader(meta); - - final fieldName = field.name.pascalCase; - final dbColumnName = (reader?.peek('name')?.stringValue ?? field.name); + final dbColumnName = _getFieldDbName(field); final fieldType = field.type.getDisplayString(withNullability: true); return Method( (m) { m - ..name = 'where$fieldName' - ..returns = refer('WhereClause<$className>') + ..name = field.name + ..returns = refer('WhereClauseValue') ..lambda = true ..requiredParameters.add(Parameter((p) => p ..name = 'value' ..type = refer(fieldType))) - ..body = Code('equal<$fieldType>("$dbColumnName", value)'); + ..body = Code('\$equal<$fieldType>("$dbColumnName", value)'); }, ); } /// This generates GetByProperty for a field Method _generateGetByPropertyMethod(FieldElement field, String className) { - final fieldName = field.name.pascalCase; + final fieldName = field.name; final fieldType = field.type.getDisplayString(withNullability: true); return Method( (m) { m - ..name = 'findBy$fieldName' + ..name = 'findBy${fieldName.pascalCase}' ..returns = refer('Future<$className?>') ..lambda = true ..requiredParameters.add(Parameter((p) => p - ..name = 'value' + ..name = 'val' ..type = refer(fieldType))) - ..body = Code('where$fieldName(value).findOne()'); + ..body = Code('findOne(where: (q) => q.$fieldName(val))'); }, ); } @@ -368,13 +388,22 @@ extension DartTypeExt on DartType { bool get isNullable => nullabilitySuffix == NullabilitySuffix.question; } +String _getFieldDbName(FieldElement element) { + final elementName = element.name; + final meta = _typeChecker(entity.TableColumn).firstAnnotationOf(element, throwOnUnresolved: false); + if (meta != null) { + return ConstantReader(meta).peek('name')?.stringValue ?? elementName; + } + return elementName; +} + extension IterableExtension on Iterable { T? firstWhereOrNull(bool Function(T element) test) { for (final element in this) { - if (test(element)) { - return element; - } + if (test(element)) return element; } return null; } } + +TypeChecker _typeChecker(Type type) => TypeChecker.fromRuntime(type); From 7bf3d8d117a5da2a893f1676752472aa8d12bc30 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sat, 13 Apr 2024 16:20:06 +0000 Subject: [PATCH 42/50] fix formatting --- .../orm/test/unit/dialects/sqlite_test.dart | 902 ------------------ packages/orm_cli/lib/src/migration.g.dart | 24 +- 2 files changed, 8 insertions(+), 918 deletions(-) delete mode 100644 packages/orm/test/unit/dialects/sqlite_test.dart diff --git a/packages/orm/test/unit/dialects/sqlite_test.dart b/packages/orm/test/unit/dialects/sqlite_test.dart deleted file mode 100644 index 074124d2..00000000 --- a/packages/orm/test/unit/dialects/sqlite_test.dart +++ /dev/null @@ -1,902 +0,0 @@ -import 'package:test/test.dart'; -import 'package:yaroorm/yaroorm.dart'; - -import '../../integration/fixtures/models.dart'; -import '../../integration/fixtures/orm_config.dart' as db; - -part 'sqlite_test.g.dart'; - -@Table('user_articles') -class Article extends Entity { - @PrimaryKey(name: '_id_') - final int id; - - final String name; - final int ownerId; - - Article(this.id, this.name, this.ownerId); -} - -@Table('article_comments') -class ArticleComment extends Entity { - @primaryKey - final int id; - - final int articleId; - final int userId; - - ArticleComment(this.id, this.articleId, this.userId); -} - -void main() async { - DB.init(db.config); - - Query.addTypeDef(userTypeData); - Query.addTypeDef
(articleTypeData); - Query.addTypeDef(article_commentTypeData); - Query.addTypeDef(postTypeData); - Query.addTypeDef(post_commentTypeData); - - late DatabaseDriver driver; - - setUpAll(() => driver = DB.driver('foo_sqlite')); - - // print(result); - - // group('SQLITE Query Builder', () { - // test('when query', () { - // final query = UserQuery.driver(driver); - - // expect(query.statement, 'SELECT * FROM users;'); - // }); - - // test('when query with single orderBy', () { - // final query = UserQuery.driver(driver).orderByDesc('names'); - - // expect(query.statement, 'SELECT * FROM users ORDER BY names DESC;'); - // }); - - // test('when query with multiple orderBy', () { - // final query = UserQuery.driver(driver).orderByDesc('names').orderByAsc('ages'); - - // expect(query.statement, 'SELECT * FROM users ORDER BY names DESC, ages ASC;'); - // }); - - // // test('when update', () { - // // final query = UserQuery.driver(driver).update( - // // where: (where) => where.where('name', '=', 'Chima'), - // // values: {'firstname': 'Chima', 'lastname': 'Precious'}, - // // ); - - // // expect(query.statement, - // // 'UPDATE users SET firstname = ?, lastname = ? WHERE name = \'Chima\';'); - // // }); - - // // test('when delete', () { - // // final query = UserQuery - // // .driver(driver) - // // .delete((where) => where.where('name', '=', 'Chima')); - - // // expect(query.statement, 'DELETE FROM users WHERE name = \'Chima\';'); - // // }); - - // // group('when .where', () { - // // test('of level 1', () { - // // final query = UserQuery.driver(driver).where('firstname', '=', 'Chima'); - - // // expect(query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\';'); - // // }); - - // // test('of level 2', () { - // // final query = UserQuery.driver(driver).whereFirstname('Chima').where('lastname', '=', 'Precious'); - - // // expect(query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname = \'Precious\';'); - // // }); - - // // test('of level 3', () { - // // final query$ = UserQuery.driver(driver).where((b) => b.equal('hello', 'Chima')).; - - // // final query = - // // UserQuery.driver(driver).whereFirstname('Chima').equal('lastname', 'Precious').where('age', '=', 22); - - // // expect(query.statement, - // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname = \'Precious\' AND age = 22;'); - // // }); - - // // group('chained with `.orWhere`', () { - // // test('of level 1', () { - // // final query = UserQuery.driver(driver).whereFirstname('Chima').orWhere('age', '=', 203); - - // // expect(query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\' OR age = 203;'); - // // }); - - // // test('of level 2', () { - // // final query = - // // UserQuery.driver(driver).whereFirstname('Chima').orWhere('age', '=', 203).where('city', '!=', 'Accra'); - - // // expect( - // // query.statement, 'SELECT * FROM users WHERE firstname = \'Chima\' OR (age = 203 AND city != \'Accra\');'); - // // }); - - // // test('of level 3', () { - // // final query = - // // UserQuery.driver(driver).where('votes', '>', 100).orWhere('name', '=', 'Abigail').where('votes', '>', 50); - - // // expect(query.statement, 'SELECT * FROM users WHERE votes > 100 OR (name = \'Abigail\' AND votes > 50);'); - // // }); - - // // test('of level 4', () { - // // final query = UserQuery.driver(driver) - // // .where('firstname', '=', 'Chima') - // // .orWhere('age', '=', 203) - // // .orWhere('city', '!=', 'Accra') - // // .where('name', 'like', 'Chima%'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE firstname = \'Chima\' OR age = 203 OR (city != \'Accra\' AND name LIKE \'Chima%\');', - // // ); - // // }); - - // // test('of level 5', () { - // // final query = UserQuery.driver(driver) - // // .where('firstname', '=', 'Chima') - // // .orWhere('age', '=', 203) - // // .orWhere('city', '!=', 'Accra') - // // .where('name', 'like', 'Chima%') - // // .where('sizes', 'between', [12, 23]); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE firstname = \'Chima\' OR age = 203 OR (city != \'Accra\' AND name LIKE \'Chima%\' AND sizes BETWEEN 12 AND 23);', - // // ); - // // }); - // // }); - - // // group('chained with', () { - // // test('.whereNull', () { - // // final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isNull('age'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND age IS NULL;', - // // ); - // // }); - - // // test('.whereNotNull', () { - // // final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isNotNull('age'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND age IS NOT NULL;', - // // ); - // // }); - - // // test('.whereIn', () { - // // final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isIn('age', [22, 24, 25]); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND age IN (22, 24, 25);', - // // ); - // // }); - - // // test('.whereNotIn', () { - // // final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isNotIn('age', [22, 24, 25]); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND age NOT IN (22, 24, 25);', - // // ); - // // }); - - // // test('.whereLike', () { - // // final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isLike('lastname', 'hello%'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname LIKE \'hello%\';', - // // ); - // // }); - - // // test('.whereNotLike', () { - // // final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isNotLike('lastname', 'hello%'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname NOT LIKE \'hello%\';', - // // ); - // // }); - - // // test('.whereBetween', () { - // // final query = UserQuery.driver(driver).where('firstname', '=', 'Chima').isBetween('lastname', [22, 50]); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname BETWEEN 22 AND 50;', - // // ); - // // }); - - // // test('.whereNotBetween', () { - // // final query = - // // UserQuery.driver(driver).where('firstname', '=', 'Chima').isNotBetween('lastname', [22.34, 50]); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE firstname = \'Chima\' AND lastname NOT BETWEEN 22.34 AND 50.0;', - // // ); - // // }); - // // }); - - // // test('with orderBy', () { - // // final query = UserQuery.driver(driver).where('name', '=', 'Chima').orderByDesc('names').orderByAsc('ages'); - - // // expect(query.statement, 'SELECT * FROM users WHERE name = \'Chima\' ORDER BY names DESC, ages ASC;'); - // // }); - // // }); - - // // group('when .whereIn', () { - // // test('of level 1', () { - // // final query = UserQuery.driver(driver).isIn('firstname', ['Accra', 'Tamale']); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE firstname IN (\'Accra\', \'Tamale\');', - // // ); - // // }); - - // // test('of level 2', () { - // // final query = UserQuery.driver(driver).isIn('places', ['Accra', 'Tamale']).where('lastname', '=', 'Precious'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\';', - // // ); - // // }); - - // // test('of level 3', () { - // // final query = UserQuery.driver(driver) - // // .isIn('places', ['Accra', 'Tamale']) - // // .where('lastname', '=', 'Precious') - // // .where('names', 'like', 'Hello%'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\' AND names LIKE \'Hello%\';', - // // ); - // // }); - - // // test('of level 4', () { - // // final query = UserQuery.driver(driver) - // // .isIn('places', ['Accra', 'Tamale']) - // // .where('lastname', '=', 'Precious') - // // .where('names', 'like', 'Hello%') - // // .orWhere('age', 'in', [23, 34, 55]); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE (places IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\' AND names LIKE \'Hello%\') OR age IN (23, 34, 55);', - // // ); - // // }); - // // }); - - // // group('when .whereNotIn', () { - // // test('of level 1', () { - // // final query = UserQuery.driver(driver).isNotIn('firstname', ['Accra', 'Tamale']); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE firstname NOT IN (\'Accra\', \'Tamale\');', - // // ); - // // }); - - // // test('of level 2', () { - // // final query = - // // UserQuery.driver(driver).isNotIn('places', ['Accra', 'Tamale']).where('lastname', '=', 'Precious'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places NOT IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\';', - // // ); - // // }); - - // // test('of level 3', () { - // // final query = UserQuery.driver(driver) - // // .isNotIn('places', ['Accra', 'Tamale']) - // // .where('lastname', '=', 'Precious') - // // .isNotNull('names'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places NOT IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\' AND names IS NOT NULL;', - // // ); - // // }); - - // // test('of level 4', () { - // // final query = UserQuery.driver(driver) - // // .isNotIn('places', ['Accra', 'Tamale']) - // // .where('lastname', '=', 'Precious') - // // .where('names', 'like', 'Hello%') - // // .isBetween('age', [23, 34]); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places NOT IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\' AND names LIKE \'Hello%\' AND age BETWEEN 23 AND 34;', - // // ); - // // }); - // // }); - - // // group('when .whereBetween', () { - // // test('should error if not supplied List with length 2', () { - // // expect( - // // () => UserQuery.driver(driver).isBetween('age', [22]).statement, - // // throwsA(isA() - // // .having((p0) => p0.message, '', 'BETWEEN requires a List with length 2 (val1, val2)'))); - // // }); - - // // test('of level 1', () { - // // final query = UserQuery.driver(driver).isBetween('age', [22, 70]); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE age BETWEEN 22 AND 70;', - // // ); - // // }); - - // // test('of level 2', () { - // // final query = - // // UserQuery.driver(driver).isBetween('places', ['Accra', 'Tamale']).where('lastname', 'between', [2, 100]); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places BETWEEN \'Accra\' AND \'Tamale\' AND lastname BETWEEN 2 AND 100;', - // // ); - // // }); - - // // test('of level 3', () { - // // final query = UserQuery.driver(driver) - // // .isIn('places', ['Accra', 'Tamale']).isBetween('lastname', [22, 48]).where('names', 'like', 'Hello%'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places IN (\'Accra\', \'Tamale\') AND lastname BETWEEN 22 AND 48 AND names LIKE \'Hello%\';', - // // ); - // // }); - - // // test('of level 4', () { - // // final query = UserQuery.driver(driver) - // // .isIn('places', ['Accra', 'Tamale']) - // // .where('lastname', '=', 'Precious') - // // .orWhere('age', 'in', [23, 34, 55]) - // // .isBetween('dates', ['2015-01-01', '2016-12-01']); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE (places IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\') OR (age IN (23, 34, 55) AND dates BETWEEN \'2015-01-01\' AND \'2016-12-01\');', - // // ); - // // }); - // // }); - - // // group('when .whereNotBetween', () { - // // test('should error if not supplied List with length 2', () { - // // expect( - // // () => UserQuery.driver(driver).isNotBetween('age', [22]).statement, - // // throwsA(isA() - // // .having((p0) => p0.message, '', 'NOT_BETWEEN requires a List with length 2 (val1, val2)'))); - // // }); - - // // test('of level 1', () { - // // final query = UserQuery.driver(driver).isNotBetween('age', [22, 70]); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE age NOT BETWEEN 22 AND 70;', - // // ); - // // }); - - // // test('of level 2', () { - // // final query = - // // UserQuery.driver(driver).isNotBetween('places', ['Accra', 'Tamale']).where('lastname', 'between', [2, 100]); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places NOT BETWEEN \'Accra\' AND \'Tamale\' AND lastname BETWEEN 2 AND 100;', - // // ); - // // }); - - // // test('of level 3', () { - // // final query = UserQuery.driver(driver) - // // .isIn('places', ['Accra', 'Tamale']).isNotBetween('lastname', [22, 48]).where('names', 'like', 'Hello%'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places IN (\'Accra\', \'Tamale\') AND lastname NOT BETWEEN 22 AND 48 AND names LIKE \'Hello%\';', - // // ); - // // }); - - // // test('of level 4', () { - // // final query = UserQuery.driver(driver) - // // .isIn('places', ['Accra', 'Tamale']) - // // .where('lastname', '=', 'Precious') - // // .orWhere('age', 'in', [23, 34, 55]) - // // .isNotBetween('dates', ['2015-01-01', '2016-12-01']); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE (places IN (\'Accra\', \'Tamale\') AND lastname = \'Precious\') OR (age IN (23, 34, 55) AND dates NOT BETWEEN \'2015-01-01\' AND \'2016-12-01\');', - // // ); - // // }); - // // }); - - // // group('when .whereLike', () { - // // test('of level 1', () { - // // final query = UserQuery.driver(driver).isLike('firstname', 'Names%%'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE firstname LIKE \'Names%%\';', - // // ); - // // }); - - // // test('of level 2', () { - // // final query = UserQuery.driver(driver).isLike('places', 'Chima**').where('lastname', '=', 'Precious'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places LIKE \'Chima**\' AND lastname = \'Precious\';', - // // ); - // // }); - - // // test('of level 3', () { - // // final query = UserQuery.driver(driver) - // // .isLike('places', 'Hello123') - // // .where('lastname', '=', 'Precious') - // // .where('names', 'like', 'Hello%'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places LIKE \'Hello123\' AND lastname = \'Precious\' AND names LIKE \'Hello%\';', - // // ); - // // }); - - // // test('of level 4', () { - // // final query = UserQuery.driver(driver) - // // .isLike('places', 'Nems#') - // // .where('lastname', '=', 'Precious') - // // .where('names', 'like', 'Hello%') - // // .orWhere('age', 'between', [23, 34]); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE (places LIKE \'Nems#\' AND lastname = \'Precious\' AND names LIKE \'Hello%\') OR age BETWEEN 23 AND 34;', - // // ); - // // }); - // // }); - - // // group('when .whereNotLike', () { - // // test('of level 1', () { - // // final query = UserQuery.driver(driver).isNotLike('firstname', 'Names%%'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE firstname NOT LIKE \'Names%%\';', - // // ); - // // }); - - // // test('of level 2', () { - // // final query = UserQuery.driver(driver).isNotLike('places', 'Chima**').isBetween('lastname', [12, 90]); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places NOT LIKE \'Chima**\' AND lastname BETWEEN 12 AND 90;', - // // ); - // // }); - - // // test('of level 3', () { - // // final query = UserQuery.driver(driver) - // // .isNotLike('places', 'Hello123') - // // .where('lastname', '=', 'Precious') - // // .where('names', 'like', 'Hello%'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places NOT LIKE \'Hello123\' AND lastname = \'Precious\' AND names LIKE \'Hello%\';', - // // ); - // // }); - - // // test('of level 4', () { - // // final query = UserQuery.driver(driver) - // // .isNotLike('places', 'Nems#') - // // .where('lastname', '=', 'Precious') - // // .orWhere('names', 'not like', 'Hello%') - // // .orWhere('age', 'between', [23, 34]); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE (places NOT LIKE \'Nems#\' AND lastname = \'Precious\') OR names NOT LIKE \'Hello%\' OR age BETWEEN 23 AND 34;', - // // ); - // // }); - // // }); - - // // group('when .whereNull', () { - // // test('of level 1', () { - // // final query = UserQuery.driver(driver).isNull('firstname'); - - // // expect(query.statement, 'SELECT * FROM users WHERE firstname IS NULL;'); - // // }); - - // // test('of level 2', () { - // // final query = UserQuery.driver(driver).isNull('places').where('lastname', '=', 'Precious'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places IS NULL AND lastname = \'Precious\';', - // // ); - // // }); - - // // test('of level 3', () { - // // final query = UserQuery.driver(driver) - // // .isNull('places') - // // .where('lastname', '=', 'Precious') - // // .where('names', 'like', 'Hello%'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places IS NULL AND lastname = \'Precious\' AND names LIKE \'Hello%\';', - // // ); - // // }); - - // // test('of level 4', () { - // // final query = UserQuery.driver(driver) - // // .isNull('places') - // // .where('lastname', '=', 'Precious') - // // .orWhere('names', 'null') - // // .orWhere('age', 'between', [23, 34]); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE (places IS NULL AND lastname = \'Precious\') OR names IS NULL OR age BETWEEN 23 AND 34;', - // // ); - // // }); - // // }); - - // // group('when .whereNotNull', () { - // // test('of level 1', () { - // // final query = UserQuery.driver(driver).isNotNull('firstname'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE firstname IS NOT NULL;', - // // ); - // // }); - - // // test('of level 2', () { - // // final query = UserQuery.driver(driver).isNotNull('places').where('lastname', '=', 'Precious'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places IS NOT NULL AND lastname = \'Precious\';', - // // ); - // // }); - - // // test('of level 3', () { - // // final query = UserQuery.driver(driver) - // // .isNotNull('places') - // // .where('lastname', '=', 'Precious') - // // .where('names', 'like', 'Hello%'); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE places IS NOT NULL AND lastname = \'Precious\' AND names LIKE \'Hello%\';', - // // ); - // // }); - - // // test('of level 4', () { - // // final query = UserQuery.driver(driver) - // // .isNotNull('places') - // // .where('lastname', '=', 'Precious') - // // .orWhere('names', 'not null') - // // .orWhere('age', 'between', [23, 34]); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE (places IS NOT NULL AND lastname = \'Precious\') OR names IS NOT NULL OR age BETWEEN 23 AND 34;', - // // ); - // // }); - // // }); - - // // test('when .whereFunc', () { - // // final query = UserQuery.driver(driver) - // // .where('name', '=', 'John') - // // .whereFunc((query) => query.where('votes', '>', 100).orWhere('title', '=', 'Admin')); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE name = \'John\' AND (votes > 100 OR title = \'Admin\');', - // // ); - // // }); - - // // group('when .orWhereFunc', () { - // // test('of level 1', () { - // // final query = UserQuery.driver(driver) - // // .where('votes', '>', 100) - // // .orWhereFunc((query) => query.where('name', '=', 'Abigail').where('votes', '>', 50)); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE votes > 100 OR (name = \'Abigail\' AND votes > 50);', - // // ); - // // }); - - // // test('of level 2', () { - // // final query = UserQuery.driver(driver) - // // .where('votes', '>', 100) - // // .orWhereFunc((query) => query.where('name', '=', 'Abigail').where('votes', '>', 50)) - // // .orWhereFunc((query) => query.where('price', '=', 'GHC200').where('votes', 'not null')); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE votes > 100 OR (name = \'Abigail\' AND votes > 50) OR (price = \'GHC200\' AND votes IS NOT NULL);', - // // ); - // // }); - - // // test('or level 3', () { - // // var query = UserQuery.driver(driver) - // // .where('votes', '>', 100) - // // .orWhereFunc((query) => query.where('name', '=', 'Abigail').where('votes', '>', 50)) - // // .where('name', '=', 22) - // // .orWhereFunc((query) => query.where('name', '=', 'Abigail')); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE votes > 100 OR (name = \'Abigail\' AND votes > 50) AND name = 22 OR name = \'Abigail\';', - // // ); - // // }); - // // }); - - // // group('.whereFunc or .orWhereFunc', () { - // // group('when used at start of query', () { - // // test('when .orWhereFunc', () { - // // expect( - // // () => UserQuery.driver(driver).orWhereFunc((query) => query.where('name', '=', 'Abigail')), - // // throwsStateError, - // // ); - // // }); - - // // test('when .whereFunc', () { - // // var query = UserQuery.driver(driver).whereFunc((query) => query.where('name', '=', 'Abigail')); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE name = \'Abigail\';', - // // ); - // // }); - // // }); - - // // test('when used together', () { - // // var query = UserQuery.driver(driver) - // // .whereFunc((query) => query.where('name', '=', 'Abigail')) - // // .orWhereFunc((query) => query.where('age', '<', 24).where('names', 'not null')); - - // // expect( - // // query.statement, - // // 'SELECT * FROM users WHERE name = \'Abigail\' OR (age < 24 AND names IS NOT NULL);', - // // ); - // // }); - - // // group('when nested crazy', () { - // // test('with level 1', () { - // // final query = UserQuery.driver(driver).where('name', '=', 'Chima').orWhereFunc( - // // (query) => query - // // .where('biscuit', '=', 'hello-world') - // // .orWhere('car_type', '=', 'lamborgini server') - // // .where('image', 'like', 'Image&&'), - // // ); - - // // expect( - // // query.statement, - // // "SELECT * FROM users WHERE name = 'Chima' OR (biscuit = 'hello-world' OR (car_type = 'lamborgini server' AND image LIKE 'Image&&'));", - // // ); - // // }); - - // // test('with level 2', () { - // // final query = UserQuery.driver(driver) - // // .where('name', '=', 'Chima') - // // .orWhereFunc( - // // (query) => query - // // .where('biscuit', '=', 'hello-world') - // // .orWhere('car_type', '=', 'lamborgini server') - // // .where('image', 'like', 'Image&&'), - // // ) - // // .whereFunc((query) => query - // // .isIn('fruits', ['oranges', 'apples']) - // // .isBetween('price', [20, 100]) - // // .equal('status', 'available') - // // .isLike('stores', 'Accra, %%')); - - // // final sB = StringBuffer(); - // // sB.write("SELECT * FROM users WHERE name = 'Chima' "); - // // sB.write("OR (biscuit = 'hello-world' OR (car_type = 'lamborgini server' AND image LIKE 'Image&&')) "); - // // sB.write( - // // "AND (fruits IN ('oranges', 'apples') AND price BETWEEN 20 AND 100 AND status = 'available' AND stores LIKE 'Accra, %%');"); - - // // expect(query.statement, sB.toString()); - // // }); - - // // test('with level 3', () { - // // final query = UserQuery.driver(driver) - // // .where('name', '=', 'Chima') - // // .orWhereFunc( - // // (query) => query - // // .where('biscuit', '=', 'hello-world') - // // .orWhere('car_type', '=', 'lamborgini server') - // // .where('image', 'like', 'Image&&'), - // // ) - // // .whereFunc((query) => query - // // .isIn('fruits', ['oranges', 'apples']) - // // .isBetween('price', [20, 100]) - // // .equal('status', 'available') - // // .isLike('stores', 'Accra, %%')) - // // .orWhereFunc((query) => query.where('languages', 'in', ['python', 'cobra']).orWhereFunc((query) => query - // // .where('job_status', '=', 'available') - // // .where('location', '=', 'Accra') - // // .isNotBetween('salary', [8000, 16000]))); - - // // final sB = StringBuffer(); - // // sB.write("SELECT * FROM users WHERE name = 'Chima' "); - // // sB.write("OR (biscuit = 'hello-world' OR (car_type = 'lamborgini server' AND image LIKE 'Image&&')) "); - // // sB.write( - // // "AND (fruits IN ('oranges', 'apples') AND price BETWEEN 20 AND 100 AND status = 'available' AND stores LIKE 'Accra, %%') "); - // // sB.write( - // // "OR (languages IN ('python', 'cobra') OR (job_status = 'available' AND location = 'Accra' AND salary NOT BETWEEN 8000 AND 16000));"); - - // // expect(query.statement, sB.toString()); - // // }); - // // }); - // // }); - // }); - - // group('SQLITE Table Blueprint', () { - // // - // group('`foreignKey` should resolve for ', () { - // // - // test('class with entity meta', () { - // final blueprint = SqliteTableBlueprint() - // ..string('name') - // ..integer('userId'); - - // late ForeignKey key; - // blueprint.foreign(onKey: (fkey) => key = fkey); - - // expect(key.table, 'user_articles'); - // expect(key.column, 'userId'); - // expect(key.foreignTable, 'users'); - // expect(key.foreignTableColumn, 'id'); - // }); - - // test('class with no meta', () { - // final blueprint = SqliteTableBlueprint()..string('userId'); - - // late ForeignKey key; - // blueprint.foreign(onKey: (fkey) => key = fkey); - - // expect(key.table, 'article_comments'); - // expect(key.column, 'userId'); - // expect(key.foreignTable, 'users'); - // expect(key.foreignTableColumn, 'id'); - // }); - - // test('custom foreign reference column', () { - // final blueprint = SqliteTableBlueprint()..string('articleId'); - - // late ForeignKey key; - // blueprint.foreign( - // column: 'articleId', onKey: (fkey) => key = fkey); - - // expect(key.table, 'article_comments'); - // expect(key.column, 'articleId'); - // expect(key.foreignTable, 'user_articles'); - // expect(key.foreignTableColumn, '_id_'); - // }); - - // test('should make statement', () { - // final blueprint = SqliteTableBlueprint() - // ..string('name') - // ..integer('userId'); - - // late ForeignKey key; - // blueprint.foreign(onKey: (fkey) => key = fkey); - - // final statement = SqliteSerializer().acceptForeignKey(blueprint, key); - // expect(statement, 'FOREIGN KEY (userId) REFERENCES users(id)'); - // }); - - // test('when custom reference actions', () { - // final blueprint = SqliteTableBlueprint() - // ..string('name') - // ..integer('userId'); - - // late ForeignKey key; - // blueprint.foreign( - // onKey: (fkey) => key = fkey.actions( - // onUpdate: ForeignKeyAction.cascade, - // onDelete: ForeignKeyAction.setNull), - // ); - - // final statement = SqliteSerializer().acceptForeignKey(blueprint, key); - // expect(statement, - // 'FOREIGN KEY (userId) REFERENCES users(id) ON UPDATE CASCADE ON DELETE SET NULL'); - // }); - - // group('when constrained', () { - // test('with no specified name', () { - // final blueprint = SqliteTableBlueprint() - // ..string('name') - // ..integer('userId'); - - // late ForeignKey key; - // blueprint.foreign( - // onKey: (fkey) => key = fkey.constrained()); - - // final statement = SqliteSerializer().acceptForeignKey(blueprint, key); - // expect( - // statement, - // 'CONSTRAINT fk_user_articles_userId_to_users_id FOREIGN KEY (userId) REFERENCES users(id)', - // ); - // }); - - // test('with specified name', () { - // final blueprint = SqliteTableBlueprint() - // ..string('name') - // ..integer('ownerId'); - - // late ForeignKey key; - // blueprint.foreign( - // column: 'ownerId', - // onKey: (fkey) => key = fkey - // .actions( - // onUpdate: ForeignKeyAction.cascade, - // onDelete: ForeignKeyAction.setNull) - // .constrained(name: 'fk_articles_users')); - - // final statement = SqliteSerializer().acceptForeignKey(blueprint, key); - // expect(statement, - // 'CONSTRAINT fk_articles_users FOREIGN KEY (ownerId) REFERENCES users(id) ON UPDATE CASCADE ON DELETE SET NULL'); - // }); - - // test('should serialize foreign key in schema', () { - // var schema = Schema.create('articles', (table) { - // return table - // ..id() - // ..string('userId') - // ..foreign( - // onKey: (key) => key.constrained(name: 'some_constraint')); - // }); - - // expect( - // schema.toScript(driver.blueprint), - // 'CREATE TABLE articles (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, userId VARCHAR NOT NULL, CONSTRAINT some_constraint FOREIGN KEY (userId) REFERENCES users(id));', - // ); - - // schema = Schema.create('articles', (table) { - // return table - // ..id(autoIncrement: false) - // ..string('ownerId') - // ..foreign( - // column: 'ownerId', - // onKey: (key) => key - // .constrained(name: 'some_constraint') - // .actions( - // onUpdate: ForeignKeyAction.cascade, - // onDelete: ForeignKeyAction.cascade)); - // }); - - // expect( - // schema.toScript(driver.blueprint), - // 'CREATE TABLE articles (id INTEGER NOT NULL PRIMARY KEY, ownerId VARCHAR NOT NULL, CONSTRAINT some_constraint FOREIGN KEY (ownerId) REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE);', - // ); - // }); - // }); - // }); - // }); -} diff --git a/packages/orm_cli/lib/src/migration.g.dart b/packages/orm_cli/lib/src/migration.g.dart index 6570d8a0..031ee7cf 100644 --- a/packages/orm_cli/lib/src/migration.g.dart +++ b/packages/orm_cli/lib/src/migration.g.dart @@ -10,8 +10,7 @@ part of 'migration.dart'; Query get MigrationEntityQuery => DB.query(); CreateSchema get MigrationEntitySchema => Schema.fromEntity(); -DBEntity get migration_entityTypeData => - DBEntity( +DBEntity get migration_entityTypeData => DBEntity( "migrations", timestampsEnabled: false, columns: [ @@ -42,20 +41,15 @@ class _$MigrationEntityEntityMirror extends EntityMirror { } class OrderMigrationEntityBy extends OrderBy { - const OrderMigrationEntityBy.migration(OrderDirection direction) - : super("migration", direction); + const OrderMigrationEntityBy.migration(OrderDirection direction) : super("migration", direction); - const OrderMigrationEntityBy.batch(OrderDirection direction) - : super("batch", direction); + const OrderMigrationEntityBy.batch(OrderDirection direction) : super("batch", direction); } extension MigrationEntityQueryExtension on Query { - Future findById(int val) => - findOne(where: (q) => q.id(val)); - Future findByMigration(String val) => - findOne(where: (q) => q.migration(val)); - Future findByBatch(int val) => - findOne(where: (q) => q.batch(val)); + Future findById(int val) => findOne(where: (q) => q.id(val)); + Future findByMigration(String val) => findOne(where: (q) => q.migration(val)); + Future findByBatch(int val) => findOne(where: (q) => q.batch(val)); Future create({ required String migration, required int batch, @@ -64,11 +58,9 @@ extension MigrationEntityQueryExtension on Query { } } -extension MigrationEntityWhereBuilderExtension - on WhereClauseBuilder { +extension MigrationEntityWhereBuilderExtension on WhereClauseBuilder { WhereClauseValue id(int value) => $equal("id", value); - WhereClauseValue migration(String value) => - $equal("migration", value); + WhereClauseValue migration(String value) => $equal("migration", value); WhereClauseValue batch(int value) => $equal("batch", value); } From e7eeaa22b2b62588b6419eb424cc76d9aaa3ed24 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 14 Apr 2024 04:20:49 +0000 Subject: [PATCH 43/50] fix mariadb tests --- .github/workflows/test.yml | 6 ++++-- packages/generator/lib/src/generator.dart | 8 ++++---- packages/orm/docker-compose.yaml | 19 +++++++++++++++++++ .../lib/src/database/driver/mysql_driver.dart | 6 +++--- packages/orm/pubspec.yaml | 6 +++--- .../test/integration/fixtures/orm_config.dart | 6 +++--- packages/orm_cli/lib/src/migration.g.dart | 8 ++------ 7 files changed, 38 insertions(+), 21 deletions(-) create mode 100644 packages/orm/docker-compose.yaml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d258e67f..5db0f9c6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,8 +44,9 @@ jobs: mariadb: image: mariadb env: - MYSQL_ROOT_PASSWORD: password + MARIADB_ROOT_PASSWORD: password MARIADB_DATABASE: test_db + MARIADB_USER: tester ports: - 3000:3306 mysql: @@ -53,6 +54,7 @@ jobs: env: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: test_db + MYSQL_USER: tester ports: - 3001:3306 postgres: @@ -60,7 +62,7 @@ jobs: env: POSTGRES_DB: test_db POSTGRES_PASSWORD: password - POSTGRES_USER: root + POSTGRES_USER: tester ports: - 3002:5432 diff --git a/packages/generator/lib/src/generator.dart b/packages/generator/lib/src/generator.dart index 25def5e4..284a2b22 100644 --- a/packages/generator/lib/src/generator.dart +++ b/packages/generator/lib/src/generator.dart @@ -142,14 +142,14 @@ class YaroormGenerator extends GeneratorForAnnotation { if (e == primaryKey.field) { return '''DBEntityField.primaryKey( - $requiredOpts) - ${!autoIncrementPrimaryKey ? ', autoIncrement: false' : ''}'''; + $requiredOpts + ${autoIncrementPrimaryKey ? ', autoIncrement: true' : ''} + )'''; } return '''DBEntityField( $requiredOpts - ${e.type.isNullable ? ', nullable: true' : ''})''' - .trim(); + ${e.type.isNullable ? ', nullable: true' : ''})'''; } final typeDataName = '${className.snakeCase}TypeData'; diff --git a/packages/orm/docker-compose.yaml b/packages/orm/docker-compose.yaml new file mode 100644 index 00000000..b0a7d16e --- /dev/null +++ b/packages/orm/docker-compose.yaml @@ -0,0 +1,19 @@ +name: orm-docker +version: "3.8" + +services: + db: + container_name: mariadb + image: mariadb:latest + environment: + MARIADB_DATABASE: test_db + MARIADB_USER: tester + MARIADB_PASSWORD: password + MARIADB_ROOT_PASSWORD: password + ports: + - "3000:3306" + volumes: + - mariadb_data:/var/lib/mariadb/data + +volumes: + mariadb_data: \ No newline at end of file diff --git a/packages/orm/lib/src/database/driver/mysql_driver.dart b/packages/orm/lib/src/database/driver/mysql_driver.dart index 996d68ef..7f9db1b0 100644 --- a/packages/orm/lib/src/database/driver/mysql_driver.dart +++ b/packages/orm/lib/src/database/driver/mysql_driver.dart @@ -135,8 +135,9 @@ class _MysqlTransactor extends DriverTransactor { @override Future>> rawQuery(String script) async { - final result = await _dbConn.execute(script); - return result.rows.map((e) => e.assoc()).toList(); + final rows = (await _dbConn.execute(script)).rows; + if (rows.isEmpty) return []; + return rows.map((e) => e.typedAssoc()).toList(); } @override @@ -184,7 +185,6 @@ class MySqlDriverTableBlueprint extends SqliteTableBlueprint { @override void id({String name = 'id', String? type, bool autoIncrement = true}) { type ??= 'INT'; - final sb = StringBuffer()..write('${_serializer.escapeStr(name)} $type NOT NULL PRIMARY KEY'); if (autoIncrement) sb.write(' AUTO_INCREMENT'); statements.add(sb.toString()); diff --git a/packages/orm/pubspec.yaml b/packages/orm/pubspec.yaml index 64eb1e62..452f72a0 100644 --- a/packages/orm/pubspec.yaml +++ b/packages/orm/pubspec.yaml @@ -15,10 +15,10 @@ environment: sdk: ^3.0.0 dependencies: - sqflite_common_ffi: - sqflite_common: ^2.5.3 + sqflite_common_ffi: ^2.3.3 + sqflite_common: ^2.5.4 mysql_client: ^0.0.27 - postgres: ^3.1.1 + postgres: ^3.2.0 meta: ^1.11.0 grammer: ^1.0.3 diff --git a/packages/orm/test/integration/fixtures/orm_config.dart b/packages/orm/test/integration/fixtures/orm_config.dart index 1c32549d..0b428713 100644 --- a/packages/orm/test/integration/fixtures/orm_config.dart +++ b/packages/orm/test/integration/fixtures/orm_config.dart @@ -17,7 +17,7 @@ final config = YaroormConfig( DatabaseDriverType.mariadb, database: 'test_db', host: 'localhost', - username: 'root', + username: 'tester', password: 'password', port: 3000, ), @@ -26,7 +26,7 @@ final config = YaroormConfig( DatabaseDriverType.mysql, database: 'test_db', host: 'localhost', - username: 'root', + username: 'tester', password: 'password', port: 3001, secure: true, @@ -36,7 +36,7 @@ final config = YaroormConfig( DatabaseDriverType.pgsql, database: 'test_db', host: 'localhost', - username: 'root', + username: 'tester', password: 'password', port: 3002, ), diff --git a/packages/orm_cli/lib/src/migration.g.dart b/packages/orm_cli/lib/src/migration.g.dart index 031ee7cf..9ad69afe 100644 --- a/packages/orm_cli/lib/src/migration.g.dart +++ b/packages/orm_cli/lib/src/migration.g.dart @@ -14,16 +14,12 @@ DBEntity get migration_entityTypeData => DBEntity MigrationEntity( - args[#id], - args[#migration], - args[#batch], - ), + build: (args) => MigrationEntity(args[#id], args[#migration], args[#batch]), ); class _$MigrationEntityEntityMirror extends EntityMirror { From c2e0e6cd3cbbab9ea8c731774a25f1c8bc89b525 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 14 Apr 2024 04:21:29 +0000 Subject: [PATCH 44/50] tiny fix --- packages/generator/lib/src/generator.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/generator/lib/src/generator.dart b/packages/generator/lib/src/generator.dart index 284a2b22..0768bf26 100644 --- a/packages/generator/lib/src/generator.dart +++ b/packages/generator/lib/src/generator.dart @@ -110,7 +110,7 @@ class YaroormGenerator extends GeneratorForAnnotation { final isReferenceField = _typeChecker(entity.reference).isExactly(meta.type!.element!); if (isReferenceField) { - final referencedType = metaReader!.peek('type')!.typeValue; + final referencedType = metaReader.peek('type')!.typeValue; final element = referencedType.element as ClassElement; final superType = element.supertype?.element; From a637fe2e81fb182ff98e3099749fa602d7ea5e6f Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 14 Apr 2024 04:29:07 +0000 Subject: [PATCH 45/50] downgrade sqlite commong ffi due to dart version --- packages/orm/docker-compose.yaml | 2 +- packages/orm/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/orm/docker-compose.yaml b/packages/orm/docker-compose.yaml index b0a7d16e..1ed0169f 100644 --- a/packages/orm/docker-compose.yaml +++ b/packages/orm/docker-compose.yaml @@ -2,7 +2,7 @@ name: orm-docker version: "3.8" services: - db: + maria_db: container_name: mariadb image: mariadb:latest environment: diff --git a/packages/orm/pubspec.yaml b/packages/orm/pubspec.yaml index 452f72a0..1f7fcb0b 100644 --- a/packages/orm/pubspec.yaml +++ b/packages/orm/pubspec.yaml @@ -15,7 +15,7 @@ environment: sdk: ^3.0.0 dependencies: - sqflite_common_ffi: ^2.3.3 + sqflite_common_ffi: ^2.3.2 sqflite_common: ^2.5.4 mysql_client: ^0.0.27 postgres: ^3.2.0 From cb5f4d8bbda6391c99d0d73b4095db5292381a0c Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 14 Apr 2024 04:50:10 +0000 Subject: [PATCH 46/50] restore sqlite related versions --- packages/orm/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/orm/pubspec.yaml b/packages/orm/pubspec.yaml index 1f7fcb0b..2037ead3 100644 --- a/packages/orm/pubspec.yaml +++ b/packages/orm/pubspec.yaml @@ -16,7 +16,7 @@ environment: dependencies: sqflite_common_ffi: ^2.3.2 - sqflite_common: ^2.5.4 + sqflite_common: ^2.3.2 mysql_client: ^0.0.27 postgres: ^3.2.0 From b0e7ebbf24bb124c4dacf33a01c8d43ab5852ea0 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 14 Apr 2024 04:54:36 +0000 Subject: [PATCH 47/50] pin sqlite version --- .github/workflows/test.yml | 6 +++--- packages/orm/pubspec.yaml | 4 ++-- packages/orm/test/integration/fixtures/orm_config.dart | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5db0f9c6..b885d834 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,7 +46,7 @@ jobs: env: MARIADB_ROOT_PASSWORD: password MARIADB_DATABASE: test_db - MARIADB_USER: tester + MARIADB_USER: root ports: - 3000:3306 mysql: @@ -54,7 +54,7 @@ jobs: env: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: test_db - MYSQL_USER: tester + MYSQL_USER: root ports: - 3001:3306 postgres: @@ -62,7 +62,7 @@ jobs: env: POSTGRES_DB: test_db POSTGRES_PASSWORD: password - POSTGRES_USER: tester + POSTGRES_USER: root ports: - 3002:5432 diff --git a/packages/orm/pubspec.yaml b/packages/orm/pubspec.yaml index 2037ead3..34575803 100644 --- a/packages/orm/pubspec.yaml +++ b/packages/orm/pubspec.yaml @@ -15,8 +15,8 @@ environment: sdk: ^3.0.0 dependencies: - sqflite_common_ffi: ^2.3.2 - sqflite_common: ^2.3.2 + sqflite_common_ffi: 2.3.0+4 + sqflite_common: 2.5.3 mysql_client: ^0.0.27 postgres: ^3.2.0 diff --git a/packages/orm/test/integration/fixtures/orm_config.dart b/packages/orm/test/integration/fixtures/orm_config.dart index 0b428713..1c32549d 100644 --- a/packages/orm/test/integration/fixtures/orm_config.dart +++ b/packages/orm/test/integration/fixtures/orm_config.dart @@ -17,7 +17,7 @@ final config = YaroormConfig( DatabaseDriverType.mariadb, database: 'test_db', host: 'localhost', - username: 'tester', + username: 'root', password: 'password', port: 3000, ), @@ -26,7 +26,7 @@ final config = YaroormConfig( DatabaseDriverType.mysql, database: 'test_db', host: 'localhost', - username: 'tester', + username: 'root', password: 'password', port: 3001, secure: true, @@ -36,7 +36,7 @@ final config = YaroormConfig( DatabaseDriverType.pgsql, database: 'test_db', host: 'localhost', - username: 'tester', + username: 'root', password: 'password', port: 3002, ), From 7bf67b376ffb3e175cefa719c3d91ffc05688f9e Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 14 Apr 2024 05:30:22 +0000 Subject: [PATCH 48/50] _ --- .github/workflows/test.yml | 9 +++++---- packages/orm/docker-compose.yaml | 17 +++++++++++++++-- .../test/integration/fixtures/orm_config.dart | 2 +- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b885d834..7ffd38e6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -49,15 +49,16 @@ jobs: MARIADB_USER: root ports: - 3000:3306 - mysql: + mysqldb: image: mysql env: - MYSQL_ROOT_PASSWORD: password + MYSQL_USER: 'tester' MYSQL_DATABASE: test_db - MYSQL_USER: root + MYSQL_PASSWORD: 'password' + MYSQL_ROOT_PASSWORD: 'password' ports: - 3001:3306 - postgres: + postgresdb: image: postgres:latest env: POSTGRES_DB: test_db diff --git a/packages/orm/docker-compose.yaml b/packages/orm/docker-compose.yaml index 1ed0169f..4e5ae888 100644 --- a/packages/orm/docker-compose.yaml +++ b/packages/orm/docker-compose.yaml @@ -2,7 +2,7 @@ name: orm-docker version: "3.8" services: - maria_db: + mariadb: container_name: mariadb image: mariadb:latest environment: @@ -14,6 +14,19 @@ services: - "3000:3306" volumes: - mariadb_data:/var/lib/mariadb/data + mysqldb: + image: mysql:latest + environment: + MYSQL_USER: 'tester' + MYSQL_PASSWORD: 'password' + MYSQL_DATABASE: test_db + MYSQL_ROOT_PASSWORD: password + ports: + - "3001:3306" + volumes: + - mysqldb_data:/var/lib/mysql_random/data + volumes: - mariadb_data: \ No newline at end of file + mariadb_data: + mysqldb_data: \ No newline at end of file diff --git a/packages/orm/test/integration/fixtures/orm_config.dart b/packages/orm/test/integration/fixtures/orm_config.dart index 1c32549d..15bade91 100644 --- a/packages/orm/test/integration/fixtures/orm_config.dart +++ b/packages/orm/test/integration/fixtures/orm_config.dart @@ -26,7 +26,7 @@ final config = YaroormConfig( DatabaseDriverType.mysql, database: 'test_db', host: 'localhost', - username: 'root', + username: 'tester', password: 'password', port: 3001, secure: true, From 079f38e1d95abb222664e9e9163300ed57cbf8e2 Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 14 Apr 2024 05:36:56 +0000 Subject: [PATCH 49/50] tiny fix --- .github/workflows/test.yml | 7 ++++--- packages/orm/test/integration/fixtures/orm_config.dart | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7ffd38e6..dad8f682 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,9 +44,10 @@ jobs: mariadb: image: mariadb env: - MARIADB_ROOT_PASSWORD: password MARIADB_DATABASE: test_db - MARIADB_USER: root + MARIADB_USER: tester + MARIADB_PASSWORD: password + MARIADB_ROOT_PASSWORD: password ports: - 3000:3306 mysqldb: @@ -63,7 +64,7 @@ jobs: env: POSTGRES_DB: test_db POSTGRES_PASSWORD: password - POSTGRES_USER: root + POSTGRES_USER: tester ports: - 3002:5432 diff --git a/packages/orm/test/integration/fixtures/orm_config.dart b/packages/orm/test/integration/fixtures/orm_config.dart index 15bade91..0b428713 100644 --- a/packages/orm/test/integration/fixtures/orm_config.dart +++ b/packages/orm/test/integration/fixtures/orm_config.dart @@ -17,7 +17,7 @@ final config = YaroormConfig( DatabaseDriverType.mariadb, database: 'test_db', host: 'localhost', - username: 'root', + username: 'tester', password: 'password', port: 3000, ), @@ -36,7 +36,7 @@ final config = YaroormConfig( DatabaseDriverType.pgsql, database: 'test_db', host: 'localhost', - username: 'root', + username: 'tester', password: 'password', port: 3002, ), From 859e80ae8f9a90629142e8ec4073a7bc8efda56c Mon Sep 17 00:00:00 2001 From: Chima Precious Date: Sun, 14 Apr 2024 05:47:02 +0000 Subject: [PATCH 50/50] improvements --- packages/orm/docker-compose.yaml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/orm/docker-compose.yaml b/packages/orm/docker-compose.yaml index 4e5ae888..3a2c413d 100644 --- a/packages/orm/docker-compose.yaml +++ b/packages/orm/docker-compose.yaml @@ -25,8 +25,19 @@ services: - "3001:3306" volumes: - mysqldb_data:/var/lib/mysql_random/data + postgresdb: + image: postgres:latest + environment: + POSTGRES_USER: 'tester' + POSTGRES_PASSWORD: 'password' + POSTGRES_DB: test_db + ports: + - "3002:3306" + volumes: + - postgresdb_data:/var/lib/psql/data volumes: mariadb_data: - mysqldb_data: \ No newline at end of file + mysqldb_data: + postgresdb_data: \ No newline at end of file