Skip to content

Commit

Permalink
eng: add type generics to repository classes (#473)
Browse files Browse the repository at this point in the history
  • Loading branch information
tshedor authored Nov 1, 2024
1 parent 4f5cc95 commit 24de905
Show file tree
Hide file tree
Showing 22 changed files with 102 additions and 148 deletions.
1 change: 1 addition & 0 deletions docs/home.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
- Example: [Simple Associations using the OfflineFirstWithRest domain](https://github.com/GetDutchie/brick/blob/main/example_rest)
- Example: [Simple Associations using the OfflineFirstWithSupabase domain](https://github.com/GetDutchie/brick/blob/main/example_supabase)
- Tutorial: [Setting up a simple app with Brick](http://www.flutterbyexample.com/#/posts/2_adding_a_repository)
- Blog: [Building offline-first mobile apps with Supabase, Flutter and Brick](https://supabase.com/blog/offline-first-flutter-apps)

## Glossary

Expand Down
12 changes: 3 additions & 9 deletions docs/offline_first/models.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ class Weight extends OfflineFirstSerdes<Map<int, String>, String> {

Some regularly requested functionality doesn't exist in out-of-the-box Brick. This functionality does not exist in the core because it is dependent on remote data formatting outside the scope of Brick or it's non-essential. However, for convenience, these features are available in a mix-and-match support library. As this is not officially supported, please use caution determining if these mixins are applicable to your implementation.

| Mixin | Description |
|---|---|
| [`DeleteAllMixin`](lib/mixins/delete_all_mixin.dart) | Adds methods `#deleteAll` and `#deleteAllExcept` |
| Mixin | Description |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`DeleteAllMixin`](lib/mixins/delete_all_mixin.dart) | Adds methods `#deleteAll` and `#deleteAllExcept` |
| [`DestructiveLocalSyncFromRemoteMixin`](lib/mixins/destructive_local_sync_from_remote_mixin.dart) | Extends `get` requests to force resync the `remoteProvider` to the local providers (also covered by new method `#destructiveLocalSyncFromRemote`) |

### General Usage
Expand All @@ -111,9 +111,3 @@ Some regularly requested functionality doesn't exist in out-of-the-box Brick. Th
import 'package:brick_offline_first/mixins.dart';
class MyRepository extends OfflineFirstRepository with DeleteAllMixin {}
```

## FAQ

### Why can't I declare a model argument?

Due to [an open analyzer bug](https://github.com/dart-lang/sdk/issues/38309), a custom model cannot be passed to the repository as a type argument.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The `OfflineFirstWithGraphql` domain uses all the same configurations and annota

![OfflineFirst#get](https://user-images.githubusercontent.com/865897/72176226-cdd8ca00-3392-11ea-867d-42f5f4620153.jpg)

!> You can change default behavior on a per-request basis using `policy:` (e.g. `get<Person>(policy: OfflineFirstUpsertPolicy.localOnly)`). This is available for `delete`, `get`, `getBatched`, `subscribe`, and `upsert`.
?> You can change default behavior on a per-request basis using `policy:` (e.g. `get<Person>(policy: OfflineFirstUpsertPolicy.localOnly)`). This is available for `delete`, `get`, `getBatched`, `subscribe`, and `upsert`.

## GraphqlOfflineQueueLink

Expand Down
2 changes: 1 addition & 1 deletion docs/offline_first/offline_first_with_rest_repository.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The `OfflineFirstWithRest` domain uses all the same configurations and annotatio

![OfflineFirst#get](https://user-images.githubusercontent.com/865897/72176226-cdd8ca00-3392-11ea-867d-42f5f4620153.jpg)

:bulb: You can change default behavior on a per-request basis using `policy:` (e.g. `get<Person>(policy: OfflineFirstUpsertPolicy.localOnly)`). This is available for `delete`, `get`, `getBatched`, `subscribe`, and `upsert`.
?> You can change default behavior on a per-request basis using `policy:` (e.g. `get<Person>(policy: OfflineFirstUpsertPolicy.localOnly)`). This is available for `delete`, `get`, `getBatched`, `subscribe`, and `upsert`.

## Generating Models from a REST Endpoint

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The `OfflineFirstWithSupabase` domain uses all the same configurations and annot

![OfflineFirst#get](https://user-images.githubusercontent.com/865897/72176226-cdd8ca00-3392-11ea-867d-42f5f4620153.jpg)

!> You can change default behavior on a per-request basis using `policy:` (e.g. `get<Person>(policy: OfflineFirstUpsertPolicy.localOnly)`). This is available for `delete`, `get`, `getBatched`, `subscribe`, and `upsert`.
?> You can change default behavior on a per-request basis using `policy:` (e.g. `get<Person>(policy: OfflineFirstUpsertPolicy.localOnly)`). This is available for `delete`, `get`, `getBatched`, `subscribe`, and `upsert`.

## Packages

Expand Down
6 changes: 0 additions & 6 deletions docs/sqlite/memory_cache_provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,3 @@ MemoryCacheProvider([Hat])
```

It is not recommended to use this provider with parent models that have child associations, as those children may be updated in the future without notifying the parent.

## FAQ

### Why can't I declare a model argument?

Due to [an open analyzer bug](https://github.com/dart-lang/sdk/issues/38309), a custom model cannot be passed to the repository as a type argument.
13 changes: 4 additions & 9 deletions packages/brick_offline_first/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Using unique identifiers, `where:` can connect multiple providers. It is declare
For a concrete example, SQLite is the local data source and REST is the remote data source:

Given the API:

```javascript
{ "assoc": {
// These don't have to map to SQLite columns.
Expand Down Expand Up @@ -118,9 +119,9 @@ class Weight extends OfflineFirstSerdes<Map<int, String>, String> {

Some regularly requested functionality doesn't exist in out-of-the-box Brick. This functionality does not exist in the core because it is dependent on remote data formatting outside the scope of Brick or it's non-essential. However, for convenience, these features are available in a mix-and-match support library. As this is not officially supported, please use caution determining if these mixins are applicable to your implementation.

| Mixin | Description |
|---|---|
| [`DeleteAllMixin`](lib/mixins/delete_all_mixin.dart) | Adds methods `#deleteAll` and `#deleteAllExcept` |
| Mixin | Description |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`DeleteAllMixin`](lib/mixins/delete_all_mixin.dart) | Adds methods `#deleteAll` and `#deleteAllExcept` |
| [`DestructiveLocalSyncFromRemoteMixin`](lib/mixins/destructive_local_sync_from_remote_mixin.dart) | Extends `get` requests to force resync the `remoteProvider` to the local providers (also covered by new method `#destructiveLocalSyncFromRemote`) |

### General Usage
Expand All @@ -147,9 +148,3 @@ final client = RestOfflineQueueClient(
![OfflineQueue logic flow](https://user-images.githubusercontent.com/865897/72175823-f44a3580-3391-11ea-8961-bbeccd74fe7b.jpg)

:warning: The queue ignores requests that are not `DELETE`, `PATCH`, `POST`, and `PUT` for REST. In GraphQL, `query` and `subscription` operations are ignored. Fetching requests are not worth tracking as the caller may have been disposed by the time the app regains connectivity.

### FAQ

#### Why can't I declare a model argument?

Due to [an open analyzer bug](https://github.com/dart-lang/sdk/issues/38309), a custom model cannot be passed to the repository as a type argument.
1 change: 1 addition & 0 deletions packages/brick_offline_first/lib/mixins.dart
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export 'package:brick_offline_first/src/mixins/delete_all_mixin.dart';
export 'package:brick_offline_first/src/mixins/destructive_local_sync_from_remote_mixin.dart';
export 'package:brick_offline_first/src/mixins/get_first_mixin.dart';
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import 'package:brick_offline_first/brick_offline_first.dart';

/// A convenience mixin for single-instance get operations.
mixin GetFirstMixin<CModel extends OfflineFirstModel> on OfflineFirstRepository<CModel> {
mixin GetFirstMixin<TRepositoryModel extends OfflineFirstModel>
on OfflineFirstRepository<TRepositoryModel> {
/// Retrieves the first instance of [TModel] with certainty that it exists.
/// If no instances exist, a [StateError] is thrown from within Dart's core
/// `Iterable#first` method. It is recommended to use [getFirstOrNull] instead.
///
/// Automatically applies `'limit': 1` to the query's `providerArgs`
Future<TModel> getFirst<TModel extends CModel>({
Future<TModel> getFirst<TModel extends TRepositoryModel>({
OfflineFirstGetPolicy policy = OfflineFirstGetPolicy.awaitRemoteWhenNoneExist,
Query? query,
bool seedOnly = false,
Expand All @@ -25,7 +26,7 @@ mixin GetFirstMixin<CModel extends OfflineFirstModel> on OfflineFirstRepository<
/// according to the [query], but returns `null` if no instances exist.
///
/// Automatically applies `'limit': 1` to the query's `providerArgs`
Future<TModel?> getFirstOrNull<TModel extends CModel>({
Future<TModel?> getFirstOrNull<TModel extends TRepositoryModel>({
OfflineFirstGetPolicy policy = OfflineFirstGetPolicy.awaitRemoteWhenNoneExist,
Query? query,
bool seedOnly = false,
Expand Down
2 changes: 2 additions & 0 deletions packages/brick_offline_first_with_graphql/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Unreleased

- Allow a generic type argument for `OfflineFirstWithGraphqlRepository`

## 3.2.0

- Add optional `onRequestException` callback function to `GraphqlOfflineQueueLink`
Expand Down
6 changes: 0 additions & 6 deletions packages/brick_offline_first_with_graphql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@ GraphqlProvider(
class MyModel extends OfflineFirstModel {}
```

### FAQ

#### Why can't I declare a model argument?

Due to [an open analyzer bug](https://github.com/dart-lang/sdk/issues/38309), a custom model cannot be passed to the repository as a type argument.

## Unsupported

### Field Types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,9 @@ import 'package:meta/meta.dart';
/// from the [remoteProvider] pass through a seperate SQLite queue. If the app
/// is unable to make contact with the [remoteProvider], the queue automatically retries in
/// sequence until it receives a response.
///
/// OfflineFirstWithGraphqlRepository should accept a type argument such as
/// <_RepositoryModel extends OfflineFirstWithGraphqlModel>, however, this causes a type bound
/// error on runtime. The argument should be reintroduced with a future version of the
/// compiler/analyzer.
abstract class OfflineFirstWithGraphqlRepository
extends OfflineFirstRepository<OfflineFirstWithGraphqlModel> {
abstract class OfflineFirstWithGraphqlRepository<
TRepositoryModel extends OfflineFirstWithGraphqlModel>
extends OfflineFirstRepository<TRepositoryModel> {
/// The type declaration is important here for the rare circumstances that
/// require interfacting with [GraphqlProvider]'s client directly.
@override
Expand Down Expand Up @@ -71,7 +67,7 @@ abstract class OfflineFirstWithGraphqlRepository
}

@override
Future<bool> delete<TModel extends OfflineFirstWithGraphqlModel>(
Future<bool> delete<TModel extends TRepositoryModel>(
TModel instance, {
Query? query,
OfflineFirstDeletePolicy policy = OfflineFirstDeletePolicy.optimisticLocal,
Expand All @@ -86,9 +82,9 @@ abstract class OfflineFirstWithGraphqlRepository
}

@override
Future<List<TModel>> get<TModel extends OfflineFirstWithGraphqlModel>({
Future<List<TModel>> get<TModel extends TRepositoryModel>({
OfflineFirstGetPolicy policy = OfflineFirstGetPolicy.awaitRemoteWhenNoneExist,
query,
Query? query,
bool seedOnly = false,
}) async {
try {
Expand All @@ -105,7 +101,7 @@ abstract class OfflineFirstWithGraphqlRepository
}

@override
Future<bool> exists<TModel extends OfflineFirstWithGraphqlModel>({Query? query}) {
Future<bool> exists<TModel extends TRepositoryModel>({Query? query}) {
try {
return super.exists<TModel>(query: query);
} on GraphQLError catch (e) {
Expand All @@ -117,7 +113,7 @@ abstract class OfflineFirstWithGraphqlRepository

@protected
@override
Future<List<TModel>> hydrate<TModel extends OfflineFirstWithGraphqlModel>({
Future<List<TModel>> hydrate<TModel extends TRepositoryModel>({
bool deserializeSqlite = true,
Query? query,
}) async {
Expand Down Expand Up @@ -156,7 +152,7 @@ abstract class OfflineFirstWithGraphqlRepository
/// with the assignment/subscription `.cancel()`'d as soon as the data is no longer needed.
/// The stream will not close naturally.
@override
Stream<List<TModel>> subscribe<TModel extends OfflineFirstWithGraphqlModel>({
Stream<List<TModel>> subscribe<TModel extends TRepositoryModel>({
OfflineFirstGetPolicy policy = OfflineFirstGetPolicy.awaitRemoteWhenNoneExist,
Query? query,
}) {
Expand Down Expand Up @@ -207,7 +203,7 @@ abstract class OfflineFirstWithGraphqlRepository
}

@override
Future<TModel> upsert<TModel extends OfflineFirstWithGraphqlModel>(
Future<TModel> upsert<TModel extends TRepositoryModel>(
TModel instance, {
OfflineFirstUpsertPolicy policy = OfflineFirstUpsertPolicy.optimisticLocal,
Query? query,
Expand Down
2 changes: 2 additions & 0 deletions packages/brick_offline_first_with_rest/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Unreleased

- Allow a generic type argument for `OfflineFirstWithRestRepository`

## 3.2.0

- Add optional `onRequestException` callback function to `RestOfflineQueueClient`
Expand Down
10 changes: 2 additions & 8 deletions packages/brick_offline_first_with_rest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,7 @@ setUpAll() async {
}
```

### FAQ

#### Why can't I declare a model argument?

Due to [an open analyzer bug](https://github.com/dart-lang/sdk/issues/38309), a custom model cannot be passed to the repository as a type argument.

## Unsupported Field Types

* Any unsupported field types from `RestProvider`, or `SqliteProvider`
* Future iterables of future models (i.e. `Future<List<Future<Model>>>`.
- Any unsupported field types from `RestProvider`, or `SqliteProvider`
- Future iterables of future models (i.e. `Future<List<Future<Model>>>`.
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,8 @@ import 'package:meta/meta.dart';
/// is unable to make contact with the [remoteProvider], the queue automatically retries in
/// sequence until it receives a response. Please note that a response may still be an error
/// code such as `404` or `500`. The queue is **only** concerned with connectivity.
///
/// OfflineFirstWithRestRepository should accept a type argument such as
/// <_RepositoryModel extends OfflineFirstWithRestModel>, however, this causes a type bound
/// error on runtime. The argument should be reintroduced with a future version of the
/// compiler/analyzer.
abstract class OfflineFirstWithRestRepository
extends OfflineFirstRepository<OfflineFirstWithRestModel> {
abstract class OfflineFirstWithRestRepository<TRepositoryModel extends OfflineFirstWithRestModel>
extends OfflineFirstRepository<TRepositoryModel> {
/// The type declaration is important here for the rare circumstances that
/// require interfacting with [RestProvider]'s client directly.
@override
Expand Down Expand Up @@ -100,7 +95,7 @@ abstract class OfflineFirstWithRestRepository
}

@override
Future<bool> delete<TModel extends OfflineFirstWithRestModel>(
Future<bool> delete<TModel extends TRepositoryModel>(
TModel instance, {
OfflineFirstDeletePolicy policy = OfflineFirstDeletePolicy.optimisticLocal,
Query? query,
Expand All @@ -120,9 +115,9 @@ abstract class OfflineFirstWithRestRepository
}

@override
Future<List<TModel>> get<TModel extends OfflineFirstWithRestModel>({
Future<List<TModel>> get<TModel extends TRepositoryModel>({
OfflineFirstGetPolicy policy = OfflineFirstGetPolicy.awaitRemoteWhenNoneExist,
query,
Query? query,
bool seedOnly = false,
}) async {
try {
Expand Down Expand Up @@ -165,7 +160,7 @@ abstract class OfflineFirstWithRestRepository
/// [OfflineFirstException] for responses that include a code within `reattemptForStatusCodes`.
/// Defaults `false`.
@override
Future<TModel> upsert<TModel extends OfflineFirstWithRestModel>(
Future<TModel> upsert<TModel extends TRepositoryModel>(
TModel instance, {
OfflineFirstUpsertPolicy policy = OfflineFirstUpsertPolicy.optimisticLocal,
Query? query,
Expand All @@ -186,7 +181,7 @@ abstract class OfflineFirstWithRestRepository

@protected
@override
Future<List<TModel>> hydrate<TModel extends OfflineFirstWithRestModel>({
Future<List<TModel>> hydrate<TModel extends TRepositoryModel>({
bool deserializeSqlite = true,
Query? query,
}) async {
Expand Down
2 changes: 1 addition & 1 deletion packages/brick_offline_first_with_rest/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ homepage: https://github.com/GetDutchie/brick/tree/main/packages/brick_offline_f
issue_tracker: https://github.com/GetDutchie/brick/issues
repository: https://github.com/GetDutchie/brick

version: 3.1.1
version: 3.2.0

environment:
sdk: ">=2.18.0 <4.0.0"
Expand Down
2 changes: 2 additions & 0 deletions packages/brick_offline_first_with_supabase/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Unreleased

- Allow a generic type argument for `OfflineFirstWithSupabaseRepository`

## 1.1.0

- Added `OfflineFirstWithSupabaseRepository#subscribeToRealtime`to sync Brick data with Supabase changes (#472, #454)
Expand Down
6 changes: 0 additions & 6 deletions packages/brick_offline_first_with_supabase/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,6 @@ Ideally, `@OfflineFirst(where:)` shouldn't be necessary to specify to make the a
final Pizza pizza;
```

### FAQ

#### Why can't I declare a model argument?

Due to [an open analyzer bug](https://github.com/dart-lang/sdk/issues/38309), a custom model cannot be passed to the repository as a type argument.

## Unsupported Field Types

- Any unsupported field types from `SupabaseProvider`, or `SqliteProvider`
Expand Down
Loading

0 comments on commit 24de905

Please sign in to comment.