diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 6b672bd..3d52b29 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -27,7 +27,7 @@ jobs: run: dart format . --set-exit-if-changed --line-length=100 - name: Run Code Gen - run: dart run build_runner build + run: dart run build_runner build --delete-conflicting-outputs - name: Lint run: dart analyze . --fatal-infos diff --git a/.gitignore b/.gitignore index 780ca52..ac86caf 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ pubspec.lock coverage/ doc/api/ +test/**.schema.dart diff --git a/test/integration/model.schema.dart b/test/integration/model.schema.dart deleted file mode 100644 index 54383d7..0000000 --- a/test/integration/model.schema.dart +++ /dev/null @@ -1,232 +0,0 @@ -part of 'model.dart'; - -extension ModelRepositories on Database { - AuthorRepository get authors => AuthorRepository._(this); - BookRepository get books => BookRepository._(this); -} - -abstract class AuthorRepository - implements - ModelRepository, - ModelRepositoryInsert, - ModelRepositoryUpdate, - ModelRepositoryDelete { - factory AuthorRepository._(Database db) = _AuthorRepository; - - Future queryAuthor(String id); - Future> queryAuthors([QueryParams? params]); -} - -class _AuthorRepository extends BaseRepository - with - RepositoryInsertMixin, - RepositoryUpdateMixin, - RepositoryDeleteMixin - implements AuthorRepository { - _AuthorRepository(super.db) : super(tableName: 'authors', keyName: 'id'); - - @override - Future queryAuthor(String id) { - return queryOne(id, AuthorQueryable()); - } - - @override - Future> queryAuthors([QueryParams? params]) { - return queryMany(AuthorQueryable(), params); - } - - @override - Future insert(List requests) async { - if (requests.isEmpty) return; - - var values = QueryValues(); - await db.query( - 'INSERT INTO "authors" ( "id", "name" )\n' - 'VALUES ${requests.map((r) => '( ${values.add(r.id)}, ${values.add(r.name)} )').join(', ')}\n', - values.values, - ); - } - - @override - Future update(List requests) async { - if (requests.isEmpty) return; - var values = QueryValues(); - await db.query( - 'UPDATE "authors"\n' - 'SET "name" = COALESCE(UPDATED."name"::text, "authors"."name")\n' - 'FROM ( VALUES ${requests.map((r) => '( ${values.add(r.id)}, ${values.add(r.name)} )').join(', ')} )\n' - 'AS UPDATED("id", "name")\n' - 'WHERE "authors"."id" = UPDATED."id"', - values.values, - ); - } -} - -abstract class BookRepository - implements - ModelRepository, - ModelRepositoryInsert, - ModelRepositoryUpdate, - ModelRepositoryDelete { - factory BookRepository._(Database db) = _BookRepository; - - Future queryBook(String id); - Future> queryBooks([QueryParams? params]); -} - -class _BookRepository extends BaseRepository - with - RepositoryInsertMixin, - RepositoryUpdateMixin, - RepositoryDeleteMixin - implements BookRepository { - _BookRepository(super.db) : super(tableName: 'books', keyName: 'id'); - - @override - Future queryBook(String id) { - return queryOne(id, BookQueryable()); - } - - @override - Future> queryBooks([QueryParams? params]) { - return queryMany(BookQueryable(), params); - } - - @override - Future insert(List requests) async { - if (requests.isEmpty) return; - - var values = QueryValues(); - await db.query( - 'INSERT INTO "books" ( "id", "title", "author_id" )\n' - 'VALUES ${requests.map((r) => '( ${values.add(r.id)}, ${values.add(r.title)}, ${values.add(r.authorId)} )').join(', ')}\n', - values.values, - ); - } - - @override - Future update(List requests) async { - if (requests.isEmpty) return; - var values = QueryValues(); - await db.query( - 'UPDATE "books"\n' - 'SET "title" = COALESCE(UPDATED."title"::text, "books"."title"), "author_id" = COALESCE(UPDATED."author_id"::text, "books"."author_id")\n' - 'FROM ( VALUES ${requests.map((r) => '( ${values.add(r.id)}, ${values.add(r.title)}, ${values.add(r.authorId)} )').join(', ')} )\n' - 'AS UPDATED("id", "title", "author_id")\n' - 'WHERE "books"."id" = UPDATED."id"', - values.values, - ); - } -} - -class AuthorInsertRequest { - AuthorInsertRequest({ - required this.id, - required this.name, - }); - - String id; - String name; -} - -class BookInsertRequest { - BookInsertRequest({ - required this.id, - required this.title, - required this.authorId, - }); - - String id; - String title; - String authorId; -} - -class AuthorUpdateRequest { - AuthorUpdateRequest({ - required this.id, - this.name, - }); - - String id; - String? name; -} - -class BookUpdateRequest { - BookUpdateRequest({ - required this.id, - this.title, - this.authorId, - }); - - String id; - String? title; - String? authorId; -} - -class AuthorQueryable extends KeyedViewQueryable { - @override - String get keyName => 'id'; - - @override - String encodeKey(String key) => TextEncoder.i.encode(key); - - @override - String get query => 'SELECT "authors".*' - 'FROM "authors"'; - - @override - String get tableAlias => 'authors'; - - @override - Author decode(TypedMap map) => AuthorView(id: map.get('id'), name: map.get('name')); -} - -class AuthorView with Author { - AuthorView({ - required this.id, - required this.name, - }); - - @override - final String id; - @override - final String name; -} - -class BookQueryable extends KeyedViewQueryable { - @override - String get keyName => 'id'; - - @override - String encodeKey(String key) => TextEncoder.i.encode(key); - - @override - String get query => 'SELECT "books".*, row_to_json("author".*) as "author"' - 'FROM "books"' - 'LEFT JOIN (${AuthorQueryable().query}) "author"' - 'ON "books"."author_id" = "author"."id"'; - - @override - String get tableAlias => 'books'; - - @override - Book decode(TypedMap map) => BookView( - id: map.get('id'), - title: map.get('title'), - author: map.get('author', AuthorQueryable().decoder)); -} - -class BookView with Book { - BookView({ - required this.id, - required this.title, - required this.author, - }); - - @override - final String id; - @override - final String title; - @override - final Author author; -} diff --git a/test/migration/schemas/schema_1.schema.dart b/test/migration/schemas/schema_1.schema.dart deleted file mode 100644 index 4a40627..0000000 --- a/test/migration/schemas/schema_1.schema.dart +++ /dev/null @@ -1,232 +0,0 @@ -part of 'schema_1.dart'; - -extension Schema1Repositories on Database { - AuthorRepository get authors => AuthorRepository._(this); - BookRepository get books => BookRepository._(this); -} - -abstract class AuthorRepository - implements - ModelRepository, - ModelRepositoryInsert, - ModelRepositoryUpdate, - ModelRepositoryDelete { - factory AuthorRepository._(Database db) = _AuthorRepository; - - Future queryAuthor(String id); - Future> queryAuthors([QueryParams? params]); -} - -class _AuthorRepository extends BaseRepository - with - RepositoryInsertMixin, - RepositoryUpdateMixin, - RepositoryDeleteMixin - implements AuthorRepository { - _AuthorRepository(super.db) : super(tableName: 'authors', keyName: 'id'); - - @override - Future queryAuthor(String id) { - return queryOne(id, AuthorQueryable()); - } - - @override - Future> queryAuthors([QueryParams? params]) { - return queryMany(AuthorQueryable(), params); - } - - @override - Future insert(List requests) async { - if (requests.isEmpty) return; - - var values = QueryValues(); - await db.query( - 'INSERT INTO "authors" ( "id", "name" )\n' - 'VALUES ${requests.map((r) => '( ${values.add(r.id)}, ${values.add(r.name)} )').join(', ')}\n', - values.values, - ); - } - - @override - Future update(List requests) async { - if (requests.isEmpty) return; - var values = QueryValues(); - await db.query( - 'UPDATE "authors"\n' - 'SET "name" = COALESCE(UPDATED."name"::text, "authors"."name")\n' - 'FROM ( VALUES ${requests.map((r) => '( ${values.add(r.id)}, ${values.add(r.name)} )').join(', ')} )\n' - 'AS UPDATED("id", "name")\n' - 'WHERE "authors"."id" = UPDATED."id"', - values.values, - ); - } -} - -abstract class BookRepository - implements - ModelRepository, - ModelRepositoryInsert, - ModelRepositoryUpdate, - ModelRepositoryDelete { - factory BookRepository._(Database db) = _BookRepository; - - Future queryBook(String id); - Future> queryBooks([QueryParams? params]); -} - -class _BookRepository extends BaseRepository - with - RepositoryInsertMixin, - RepositoryUpdateMixin, - RepositoryDeleteMixin - implements BookRepository { - _BookRepository(super.db) : super(tableName: 'books', keyName: 'id'); - - @override - Future queryBook(String id) { - return queryOne(id, BookQueryable()); - } - - @override - Future> queryBooks([QueryParams? params]) { - return queryMany(BookQueryable(), params); - } - - @override - Future insert(List requests) async { - if (requests.isEmpty) return; - - var values = QueryValues(); - await db.query( - 'INSERT INTO "books" ( "id", "title", "author_id" )\n' - 'VALUES ${requests.map((r) => '( ${values.add(r.id)}, ${values.add(r.title)}, ${values.add(r.authorId)} )').join(', ')}\n', - values.values, - ); - } - - @override - Future update(List requests) async { - if (requests.isEmpty) return; - var values = QueryValues(); - await db.query( - 'UPDATE "books"\n' - 'SET "title" = COALESCE(UPDATED."title"::text, "books"."title"), "author_id" = COALESCE(UPDATED."author_id"::text, "books"."author_id")\n' - 'FROM ( VALUES ${requests.map((r) => '( ${values.add(r.id)}, ${values.add(r.title)}, ${values.add(r.authorId)} )').join(', ')} )\n' - 'AS UPDATED("id", "title", "author_id")\n' - 'WHERE "books"."id" = UPDATED."id"', - values.values, - ); - } -} - -class AuthorInsertRequest { - AuthorInsertRequest({ - required this.id, - required this.name, - }); - - String id; - String name; -} - -class BookInsertRequest { - BookInsertRequest({ - required this.id, - required this.title, - required this.authorId, - }); - - String id; - String title; - String authorId; -} - -class AuthorUpdateRequest { - AuthorUpdateRequest({ - required this.id, - this.name, - }); - - String id; - String? name; -} - -class BookUpdateRequest { - BookUpdateRequest({ - required this.id, - this.title, - this.authorId, - }); - - String id; - String? title; - String? authorId; -} - -class AuthorQueryable extends KeyedViewQueryable { - @override - String get keyName => 'id'; - - @override - String encodeKey(String key) => TextEncoder.i.encode(key); - - @override - String get query => 'SELECT "authors".*' - 'FROM "authors"'; - - @override - String get tableAlias => 'authors'; - - @override - Author decode(TypedMap map) => AuthorView(id: map.get('id'), name: map.get('name')); -} - -class AuthorView with Author { - AuthorView({ - required this.id, - required this.name, - }); - - @override - final String id; - @override - final String name; -} - -class BookQueryable extends KeyedViewQueryable { - @override - String get keyName => 'id'; - - @override - String encodeKey(String key) => TextEncoder.i.encode(key); - - @override - String get query => 'SELECT "books".*, row_to_json("author".*) as "author"' - 'FROM "books"' - 'LEFT JOIN (${AuthorQueryable().query}) "author"' - 'ON "books"."author_id" = "author"."id"'; - - @override - String get tableAlias => 'books'; - - @override - Book decode(TypedMap map) => BookView( - id: map.get('id'), - title: map.get('title'), - author: map.get('author', AuthorQueryable().decoder)); -} - -class BookView with Book { - BookView({ - required this.id, - required this.title, - required this.author, - }); - - @override - final String id; - @override - final String title; - @override - final Author author; -} diff --git a/test/migration/schemas/schema_2.schema.dart b/test/migration/schemas/schema_2.schema.dart deleted file mode 100644 index fbf9000..0000000 --- a/test/migration/schemas/schema_2.schema.dart +++ /dev/null @@ -1,240 +0,0 @@ -part of 'schema_2.dart'; - -extension Schema2Repositories on Database { - AuthorRepository get authors => AuthorRepository._(this); - BookRepository get books => BookRepository._(this); -} - -abstract class AuthorRepository - implements - ModelRepository, - ModelRepositoryInsert, - ModelRepositoryUpdate, - ModelRepositoryDelete { - factory AuthorRepository._(Database db) = _AuthorRepository; - - Future queryAuthor(String id); - Future> queryAuthors([QueryParams? params]); -} - -class _AuthorRepository extends BaseRepository - with - RepositoryInsertMixin, - RepositoryUpdateMixin, - RepositoryDeleteMixin - implements AuthorRepository { - _AuthorRepository(super.db) : super(tableName: 'authors', keyName: 'id'); - - @override - Future queryAuthor(String id) { - return queryOne(id, AuthorQueryable()); - } - - @override - Future> queryAuthors([QueryParams? params]) { - return queryMany(AuthorQueryable(), params); - } - - @override - Future insert(List requests) async { - if (requests.isEmpty) return; - - var values = QueryValues(); - await db.query( - 'INSERT INTO "authors" ( "id", "name" )\n' - 'VALUES ${requests.map((r) => '( ${values.add(r.id)}, ${values.add(r.name)} )').join(', ')}\n', - values.values, - ); - } - - @override - Future update(List requests) async { - if (requests.isEmpty) return; - var values = QueryValues(); - await db.query( - 'UPDATE "authors"\n' - 'SET "name" = COALESCE(UPDATED."name"::text, "authors"."name")\n' - 'FROM ( VALUES ${requests.map((r) => '( ${values.add(r.id)}, ${values.add(r.name)} )').join(', ')} )\n' - 'AS UPDATED("id", "name")\n' - 'WHERE "authors"."id" = UPDATED."id"', - values.values, - ); - } -} - -abstract class BookRepository - implements - ModelRepository, - ModelRepositoryInsert, - ModelRepositoryUpdate, - ModelRepositoryDelete { - factory BookRepository._(Database db) = _BookRepository; - - Future queryBook(String id); - Future> queryBooks([QueryParams? params]); -} - -class _BookRepository extends BaseRepository - with - RepositoryInsertMixin, - RepositoryUpdateMixin, - RepositoryDeleteMixin - implements BookRepository { - _BookRepository(super.db) : super(tableName: 'books', keyName: 'id'); - - @override - Future queryBook(String id) { - return queryOne(id, BookQueryable()); - } - - @override - Future> queryBooks([QueryParams? params]) { - return queryMany(BookQueryable(), params); - } - - @override - Future insert(List requests) async { - if (requests.isEmpty) return; - - var values = QueryValues(); - await db.query( - 'INSERT INTO "books" ( "id", "title", "rating", "author_id" )\n' - 'VALUES ${requests.map((r) => '( ${values.add(r.id)}, ${values.add(r.title)}, ${values.add(r.rating)}, ${values.add(r.authorId)} )').join(', ')}\n', - values.values, - ); - } - - @override - Future update(List requests) async { - if (requests.isEmpty) return; - var values = QueryValues(); - await db.query( - 'UPDATE "books"\n' - 'SET "title" = COALESCE(UPDATED."title"::text, "books"."title"), "rating" = COALESCE(UPDATED."rating"::int8, "books"."rating"), "author_id" = COALESCE(UPDATED."author_id"::text, "books"."author_id")\n' - 'FROM ( VALUES ${requests.map((r) => '( ${values.add(r.id)}, ${values.add(r.title)}, ${values.add(r.rating)}, ${values.add(r.authorId)} )').join(', ')} )\n' - 'AS UPDATED("id", "title", "rating", "author_id")\n' - 'WHERE "books"."id" = UPDATED."id"', - values.values, - ); - } -} - -class AuthorInsertRequest { - AuthorInsertRequest({ - required this.id, - required this.name, - }); - - String id; - String name; -} - -class BookInsertRequest { - BookInsertRequest({ - required this.id, - required this.title, - required this.rating, - required this.authorId, - }); - - String id; - String title; - int rating; - String authorId; -} - -class AuthorUpdateRequest { - AuthorUpdateRequest({ - required this.id, - this.name, - }); - - String id; - String? name; -} - -class BookUpdateRequest { - BookUpdateRequest({ - required this.id, - this.title, - this.rating, - this.authorId, - }); - - String id; - String? title; - int? rating; - String? authorId; -} - -class AuthorQueryable extends KeyedViewQueryable { - @override - String get keyName => 'id'; - - @override - String encodeKey(String key) => TextEncoder.i.encode(key); - - @override - String get query => 'SELECT "authors".*' - 'FROM "authors"'; - - @override - String get tableAlias => 'authors'; - - @override - Author decode(TypedMap map) => AuthorView(id: map.get('id'), name: map.get('name')); -} - -class AuthorView with Author { - AuthorView({ - required this.id, - required this.name, - }); - - @override - final String id; - @override - final String name; -} - -class BookQueryable extends KeyedViewQueryable { - @override - String get keyName => 'id'; - - @override - String encodeKey(String key) => TextEncoder.i.encode(key); - - @override - String get query => 'SELECT "books".*, row_to_json("author".*) as "author"' - 'FROM "books"' - 'LEFT JOIN (${AuthorQueryable().query}) "author"' - 'ON "books"."author_id" = "author"."id"'; - - @override - String get tableAlias => 'books'; - - @override - Book decode(TypedMap map) => BookView( - id: map.get('id'), - title: map.get('title'), - rating: map.get('rating'), - author: map.get('author', AuthorQueryable().decoder)); -} - -class BookView with Book { - BookView({ - required this.id, - required this.title, - required this.rating, - required this.author, - }); - - @override - final String id; - @override - final String title; - @override - final int rating; - @override - final Author author; -}