Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(supabase): refetch associations for realtime subscriptions (#514) #517

Merged
merged 7 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 40 additions & 28 deletions MIGRATING.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,59 @@
# Migrating Between Major Versions

## Migrating from Brick 3 to Brick 4
In preparation for Brick 4, `Query` is migrating away from loosely-defined arguments in favor of standardized fields that can be easily deprecated and discovered by analysis.

Brick 4 away from loosely-defined `Query` arguments in favor of standardized fields that can be easily deprecated and discovered by analysis.
**`providerArgs` will be supported until Brick 4 is officially released**.

### Breaking Changes
It is still recommended you migrate to the new `Query` for new features and long-term support.

**`providerArgs` will be supported until Brick 4 is officially released**. It is still recommended you migrate to the new `Query` for better `orderBy` and `limitBy`.
## Improvements

- `Query(providerArgs: {'limit':})` is now `Query(limit:)`
- `Query(providerArgs: {'offset':})` is now `Query(offset:)`
- `Query(providerArgs: {'orderBy':})` is now `Query(orderBy:)`. This is a more significant change than `limit` or `offset`. `orderBy` is now defined by a class that permits multiple commands. For example, `'orderBy': 'name ASC'` becomes `[OrderBy('name', ascending: true)]`. First-class Brick providers (SQLite and Supabase) also support association-based querying by declaring a `model:`.
- `Query#orderBy` will support association ordering and multiple values
- `Query` is constructed with `const`
- `Query#offset` no longer requires companion `limit` parameter
- `brick_sqlite` and `brick_supabase` support association ordering. For example, `Query(orderBy: [OrderBy.desc('assoc', associationField: 'name')])` on `DemoModel` will produce the following SQL statement:
```sql
'SELECT DISTINCT `DemoModel`.* FROM `DemoModel` ORDER BY `DemoModelAssoc`.name DESC'
```
- `brick_supabase` supports advanced limiting. For example, `Query(limitBy: [LimitBy(1, evaluatedField: 'assoc'))` is the equivalent of `.limit(1, referencedTable: 'demo_model')`

#### brick_graphql
## Universal Deprecations

- `Query(providerArgs: {'context':})` is now `Query(forProviders: [GraphqlProviderQuery(context:)])`
- `Query(providerArgs: {'operation':})` is now `Query(forProviders: [GraphqlProviderQuery(operation:)])`
| Old | New | Notes |
| ----------------------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Query(providerArgs: {'limit':})` | `Query(limit:)` | `limit` and `limitBy` may be used together, however, `limitBy` will only limit `evaluatedField:` associations |
| `Query(providerArgs: {'offset':})` | `Query(offset:)` | |
| `Query(providerArgs: {'orderBy':})` | `Query(orderBy:)` | `orderBy` is now defined by a class that permits multiple commands. For example, `'orderBy': 'name ASC'` becomes `[OrderBy('name', ascending: true)]`. First-class Brick providers (SQLite and Supabase) also support association-based querying by declaring a `associationField:`. This `associationField` is optional in Supabase but required for `SQLite`. |

#### brick_rest
## Package-specific deprecations

- `Query(providerArgs: {'request':})` is now `Query(forProviders: [RestProviderQuery(request:)])`. This is a similarly significant chang that allows providers to be detected by static analysis and reduces the need for manual documentation.
### brick_graphql

#### brick_sqlite
| Old | New | Notes |
| ------------------------------------- | --------------------------------------------------------- | ----- |
| `Query(providerArgs: {'context':})` | `Query(forProviders: [GraphqlProviderQuery(context:)])` |
| `Query(providerArgs: {'operation':})` | `Query(forProviders: [GraphqlProviderQuery(operation:)])` |

- `Query(providerArgs: {'collate':})` is now `Query(forProviders: [SqliteProviderQuery(collate:)])`
- `Query(providerArgs: {'having':})` is now `Query(forProviders: [SqliteProviderQuery(having:)])`
- `Query(providerArgs: {'groupBy':})` is now `Query(forProviders: [SqliteProviderQuery(groupBy:)])`
### brick_rest

#### brick_supabase
| Old | New | Notes |
| ----------------------------------- | ---------------------------------------------------- | ----- |
| `Query(providerArgs: {'request':})` | `Query(forProviders: [RestProviderQuery(request:)])` | |

- `Query(providerArgs: {'limitReferencedTable':})` has been removed in favor of `Query(limitBy:)`
- `Query(providerArgs: {'orderByReferencedTable':})` has been removed in favor of `Query(orderBy:)`
### brick_sqlite

### Improvements
| Old | New | Notes |
| ----------------------------------- | ------------------------------------------------------ | ----- |
| `Query(providerArgs: {'collate':})` | `Query(forProviders: [SqliteProviderQuery(collate:)])` |
| `Query(providerArgs: {'having':})` | `Query(forProviders: [SqliteProviderQuery(having:)])` |
| `Query(providerArgs: {'groupBy':})` | `Query(forProviders: [SqliteProviderQuery(groupBy:)])` |

- `OrderBy` will support association ordering and multiple values
- `Query` is constructed with `const`
- `Query#offset` no longer requires companion `limit` parameter
- `brick_sqlite` and `brick_supabase` support association ordering. For example, `Query(orderBy: [OrderBy.desc('name', model: DemoModelAssoc)])` on `DemoModel` will produce the following SQL statement:
```sql
'SELECT DISTINCT `DemoModel`.* FROM `DemoModel` ORDER BY `DemoModelAssoc`.name DESC'
```
- `brick_supabase` supports advanced limiting. For example, `Query(limitBy: [LimitBy(1, model: DemoModel))` is the equivalent of `.limit(1, referencedTable: 'demo_model')`
### brick_supabase

| Old | New | Notes |
| -------------------------------------------------- | --- | ------------------------------------- |
| `Query(providerArgs: {'limitReferencedTable':})` | | Removed in favor of `Query(limitBy:)` |
| `Query(providerArgs: {'orderByReferencedTable':})` | | Removed in favor of `Query(orderBy:)` |

## Migrating from Brick 2 to Brick 3

Expand Down
4 changes: 4 additions & 0 deletions packages/brick_build/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
## Unreleased

## 3.3.0

- Add documentation to increase pub.dev score
- Update minimum `brick_core` to `1.3.0`
- Update analysis to modern lints

## 3.2.1

Expand Down
4 changes: 2 additions & 2 deletions packages/brick_build/lib/src/builders/adapter_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:brick_build/src/utils/string_helpers.dart';
import 'package:build/build.dart';

/// Writes adapter code (model serialization/deserialization).
/// Outputs to brick/adapters/<MODEL>_adapter.g.dart
/// Outputs to brick/adapters/{MODEL}_adapter.g.dart
class AdapterBuilder<_ClassAnnotation> extends BaseBuilder<_ClassAnnotation> {
///
final AnnotationSuperGenerator generator;
Expand All @@ -12,7 +12,7 @@ class AdapterBuilder<_ClassAnnotation> extends BaseBuilder<_ClassAnnotation> {
final outputExtension = '.adapter_builder.dart';

/// Writes adapter code (model serialization/deserialization).
/// Outputs to brick/adapters/<MODEL>_adapter.g.dart
/// Outputs to brick/adapters/{MODEL}_adapter.g.dart
AdapterBuilder(this.generator);

@override
Expand Down
10 changes: 5 additions & 5 deletions packages/brick_build/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ homepage: https://github.com/GetDutchie/brick/tree/main/packages/brick_build
issue_tracker: https://github.com/GetDutchie/brick/issues
repository: https://github.com/GetDutchie/brick

version: 3.2.1
version: 3.3.0

environment:
sdk: ">=2.18.0 <4.0.0"

dependencies:
analyzer: ">=6.0.0 <7.0.0"
brick_core: ^1.1.1
brick_core: ^1.3.0
build: ^2.3.0
dart_style: ">=2.0.0 <3.0.0"
glob: ">=2.1.0 <3.0.0"
Expand All @@ -23,6 +23,6 @@ dependencies:
dev_dependencies:
brick_build_test:
path: ../brick_build_test
build_verify: ^2.0.0
lints: ^2.0.1
test: ^1.20.1
build_verify:
lints:
test:
15 changes: 15 additions & 0 deletions packages/brick_core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
## Unreleased

## 1.3.1

- `const`antize `Where.exactly` and `OrderBy.{desc|asc}`
- Add deprecation annotation to `Query#copyWith#providerArgs`

## 1.3.0

- **DEPRECATION** `Query(providerArgs: {'limit':})` is now `Query(limit:)`
- **DEPRECATION** `Query(providerArgs: {'offset':})` is now `Query(offset:)`
- **DEPRECATION** `Query(providerArgs: {'orderBy':})` is now `Query(orderBy:)`. `orderBy` is now defined by a class that permits multiple commands. For example, `'orderBy': 'name ASC'` becomes `[OrderBy('name', ascending: true)]`.
- **DEPRECATION** `providerArgs` will be removed in the next major release
- `OrderBy` will support association ordering and multiple values
- `Query` is constructed with `const`
- `Query#offset` no longer requires companion `limit` parameter

## 1.2.1

- Add `FieldRename` to `FieldSerializable`
Expand Down
22 changes: 11 additions & 11 deletions packages/brick_core/lib/src/query/limit_by.dart
Original file line number Diff line number Diff line change
@@ -1,43 +1,43 @@
import 'dart:convert';

import 'package:brick_core/src/model.dart';
import 'package:brick_core/src/model_dictionary.dart';
import 'package:brick_core/src/provider.dart';
import 'package:brick_core/src/adapter.dart';

/// Construct directions for a provider to limit its results.
class LimitBy {
/// The ceiling for how many results can be returned for a [model].
/// The ceiling for how many results can be returned for [evaluatedField].
final int amount;

/// Some providers may support limiting based on a model retrieved by the query.
/// This [Model] should be accessible to the [Provider]'s [ModelDictionary].
final Type model;
/// This Dart field name should be accessible to the [Adapter]'s definitions
/// (e.g. a `RuntimeSqliteColumnDefinition` map).
final String evaluatedField;

/// Construct directions for a provider to limit its results.
const LimitBy(
this.amount, {
required this.model,
required this.evaluatedField,
});

/// Construct a [LimitBy] from a JSON map.
factory LimitBy.fromJson(Map<String, dynamic> json) => LimitBy(
json['amount'],
model: json['model'],
evaluatedField: json['evaluatedField'],
);

/// Serialize to JSON
Map<String, dynamic> toJson() => {
'amount': amount,
'model': model,
'evaluatedField': evaluatedField,
};

@override
String toString() => jsonEncode(toJson());

@override
bool operator ==(Object other) =>
identical(this, other) || other is LimitBy && amount == other.amount && model == other.model;
identical(this, other) ||
other is LimitBy && amount == other.amount && evaluatedField == other.evaluatedField;

@override
int get hashCode => amount.hashCode ^ model.hashCode;
int get hashCode => amount.hashCode ^ evaluatedField.hashCode;
}
27 changes: 12 additions & 15 deletions packages/brick_core/lib/src/query/order_by.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import 'package:brick_core/src/model.dart';
import 'package:brick_core/src/model_dictionary.dart';
import 'package:brick_core/src/provider.dart';
import 'package:meta/meta.dart';

/// Construct directions for a provider to sort its results.
@immutable
class OrderBy {
/// Defaults to `true`.
final bool ascending;
Expand All @@ -15,36 +11,37 @@ class OrderBy {
/// and the remote source's expected name.
final String evaluatedField;

/// Some providers may support ordering based on a model retrieved by the query.
/// This [Model] should be accessible to the [Provider]'s [ModelDictionary].
final Type? model;
/// The Dart name of the field of the association model
/// if the [evaluatedField] is an association.
///
/// If [evaluatedField] is not an association, this should be `null`.
final String? associationField;

/// Construct directions for a provider to sort its results.
const OrderBy(
this.evaluatedField, {
this.ascending = true,
this.model,
this.associationField,
});

/// Sort by [ascending] order (A-Z).
factory OrderBy.asc(String evaluatedField, {Type? model}) =>
OrderBy(evaluatedField, model: model);
const OrderBy.asc(this.evaluatedField, {this.associationField}) : ascending = true;

/// Sort by descending order (Z-A).
factory OrderBy.desc(String evaluatedField, {Type? model}) =>
OrderBy(evaluatedField, ascending: false, model: model);
const OrderBy.desc(this.evaluatedField, {this.associationField}) : ascending = false;

/// Construct an [OrderBy] from a JSON map.
factory OrderBy.fromJson(Map<String, dynamic> json) => OrderBy(
json['evaluatedField'],
ascending: json['ascending'],
associationField: json['associationField'],
);

/// Serialize to JSON
Map<String, dynamic> toJson() => {
'ascending': ascending,
if (associationField != null) 'associationField': associationField,
'evaluatedField': evaluatedField,
if (model != null) 'model': model?.toString(),
};

@override
Expand All @@ -56,8 +53,8 @@ class OrderBy {
other is OrderBy &&
evaluatedField == other.evaluatedField &&
ascending == other.ascending &&
model == other.model;
associationField == other.associationField;

@override
int get hashCode => evaluatedField.hashCode ^ ascending.hashCode ^ model.hashCode;
int get hashCode => evaluatedField.hashCode ^ ascending.hashCode ^ associationField.hashCode;
}
1 change: 1 addition & 0 deletions packages/brick_core/lib/src/query/query.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class Query {
List<LimitBy>? limitBy,
int? offset,
List<OrderBy>? orderBy,
@Deprecated('Use limit, offset, limitBy, orderBy, or forProviders instead.')
Map<String, dynamic>? providerArgs,
List<WhereCondition>? where,
}) =>
Expand Down
5 changes: 3 additions & 2 deletions packages/brick_core/lib/src/query/where.dart
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,9 @@ class Where extends WhereCondition {
conditions = null;

/// A condition written with brevity. [isRequired] defaults `true`.
factory Where.exact(String evaluatedField, value, {bool isRequired = true}) =>
Where(evaluatedField, value: value, compare: Compare.exact, isRequired: isRequired);
const Where.exact(this.evaluatedField, this.value, {this.isRequired = true})
: compare = Compare.exact,
conditions = null;

/// Convenience function to create a [Where] with [Compare.exact].
Where isExactly(dynamic value) =>
Expand Down
9 changes: 4 additions & 5 deletions packages/brick_core/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ homepage: https://github.com/GetDutchie/brick/tree/main/packages/brick_core
issue_tracker: https://github.com/GetDutchie/brick/issues
repository: https://github.com/GetDutchie/brick

version: 2.0.0
version: 1.3.1

environment:
sdk: ">=3.0.0 <4.0.0"
Expand All @@ -13,7 +13,6 @@ dependencies:
collection: ">=1.15.0 <2.0.0"

dev_dependencies:
dart_style: ">=2.0.0 <3.0.0"
lints: ">=4.0.0 <6.0.0"
mockito: ^5.0.0
test: ^1.16.5
lints:
mockito:
test:
16 changes: 6 additions & 10 deletions packages/brick_core/test/query/limit_by_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,22 @@ void main() {
group('LimitBy', () {
test('equality', () {
expect(
const LimitBy(2, model: num),
LimitBy.fromJson(const {'amount': 2, 'model': num}),
const LimitBy(2, evaluatedField: 'name'),
LimitBy.fromJson(const {'amount': 2, 'evaluatedField': 'name'}),
);
});

test('#toJson', () {
expect(
const LimitBy(2, model: int).toJson(),
{'amount': 2, 'model': int},
);
expect(
const LimitBy(2, model: String).toJson(),
{'amount': 2, 'model': String},
const LimitBy(2, evaluatedField: 'name').toJson(),
{'amount': 2, 'evaluatedField': 'name'},
);
});

test('.fromJson', () {
expect(
LimitBy.fromJson(const {'amount': 2, 'model': String}),
const LimitBy(2, model: String),
LimitBy.fromJson(const {'amount': 2, 'evaluatedField': 'longerName'}),
const LimitBy(2, evaluatedField: 'longerName'),
);
});
});
Expand Down
Loading