Skip to content

Commit

Permalink
feat(supabase): define upsertMethod within query for supabase (#529)
Browse files Browse the repository at this point in the history
  • Loading branch information
tshedor authored Jan 13, 2025
1 parent 0635cf1 commit 9a41b95
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 3 deletions.
6 changes: 6 additions & 0 deletions packages/brick_supabase/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
## Unreleased

## 1.4.1+1

- Add `SupabaseProviderQuery`
- Support defining `upsertMethod` via `SupabaseProviderQuery`
- Fix `orderBy` queries to use the column name instead of the field name when constructing PostgREST queries

## 1.4.0

- **DEPRECATION** remove `Supabase#nullable`. Builders should evaluate the nullable suffix of the field instead
Expand Down
1 change: 1 addition & 0 deletions packages/brick_supabase/lib/brick_supabase.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export 'package:brick_supabase/src/supabase_adapter.dart';
export 'package:brick_supabase/src/supabase_model.dart';
export 'package:brick_supabase/src/supabase_model_dictionary.dart';
export 'package:brick_supabase/src/supabase_provider.dart';
export 'package:brick_supabase/src/supabase_provider_query.dart';
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,12 @@ class QuerySupabaseTransformer<_Model extends SupabaseModel> {
return query!.orderBy.fold(withProviderArgs, (acc, orderBy) {
final definition = adapter.fieldsToSupabaseColumns[orderBy.evaluatedField];
final tableName = modelDictionary.adapterFor[definition?.associationType]?.supabaseTableName;
final columnName = adapter
.fieldsToSupabaseColumns[orderBy.associationField ?? orderBy.evaluatedField]?.columnName;

final url = acc.appendSearchParams(
tableName == null ? 'order' : '$tableName.order',
'${orderBy.associationField ?? orderBy.evaluatedField}.${orderBy.ascending ? 'asc' : 'desc'}.nullslast',
'$columnName.${orderBy.ascending ? 'asc' : 'desc'}.nullslast',
);
return acc.copyWithUrl(url);
});
Expand Down
3 changes: 3 additions & 0 deletions packages/brick_supabase/lib/src/supabase_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:brick_core/core.dart';
import 'package:brick_supabase/src/query_supabase_transformer.dart';
import 'package:brick_supabase/src/supabase_model.dart';
import 'package:brick_supabase/src/supabase_model_dictionary.dart';
import 'package:brick_supabase/src/supabase_provider_query.dart';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:supabase/supabase.dart';
Expand Down Expand Up @@ -149,9 +150,11 @@ class SupabaseProvider implements Provider<SupabaseModel> {
}) async {
final adapter = modelDictionary.adapterFor[TModel]!;
final output = await adapter.toSupabase(instance, provider: this, repository: repository);
final providerQuery = query?.providerQueries[SupabaseProvider] as SupabaseProviderQuery?;

return await recursiveAssociationUpsert(
output,
method: providerQuery?.upsertMethod ?? UpsertMethod.upsert,
type: TModel,
query: query,
repository: repository,
Expand Down
30 changes: 30 additions & 0 deletions packages/brick_supabase/lib/src/supabase_provider_query.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import 'package:brick_core/core.dart';
import 'package:brick_supabase/src/supabase_provider.dart';

/// [SupabaseProvider]-specific options for a [Query]
class SupabaseProviderQuery extends ProviderQuery<SupabaseProvider> {
/// An internal definition for remote requests.
/// In rare cases, a specific `update` or `insert` is preferable to `upsert`;
/// this enum explicitly declares the desired behavior.
final UpsertMethod? upsertMethod;

/// [SupabaseProvider]-specific options for a [Query]
const SupabaseProviderQuery({
this.upsertMethod,
});

@override
Map<String, dynamic> toJson() => {
if (upsertMethod != null) 'upsertMethod': upsertMethod?.name,
};

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SupabaseProviderQuery &&
runtimeType == other.runtimeType &&
upsertMethod == other.upsertMethod;

@override
int get hashCode => upsertMethod.hashCode;
}
2 changes: 1 addition & 1 deletion packages/brick_supabase/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_supabase
issue_tracker: https://github.com/GetDutchie/brick/issues
repository: https://github.com/GetDutchie/brick

version: 1.4.0
version: 1.4.1+1

environment:
sdk: ">=3.0.0 <4.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ void main() {

expect(
transformBuilder.query,
'select=id,nested_column:demo_associations(id,name,assoc_id:demos!assoc_id(id,name,custom_age),assocs:demos(id,name,custom_age))&demo_associations.order=nested.desc.nullslast',
'select=id,nested_column:demo_associations(id,name,assoc_id:demos!assoc_id(id,name,custom_age),assocs:demos(id,name,custom_age))&demo_associations.order=nested_column.desc.nullslast',
);
});

Expand Down
24 changes: 24 additions & 0 deletions packages/brick_supabase/test/supabase_provider_test.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// ignore_for_file: unawaited_futures

import 'package:brick_core/query.dart';
import 'package:brick_supabase/src/supabase_provider.dart';
import 'package:brick_supabase/src/supabase_provider_query.dart';
import 'package:brick_supabase/testing.dart';
import 'package:test/test.dart';

Expand Down Expand Up @@ -141,6 +143,28 @@ void main() {
expect(inserted.assoc.id, instance.assoc.id);
expect(inserted.name, instance.name);
});

test('with non-default method from query', () async {
const req = SupabaseRequest<Demo>(
requestMethod: 'PATCH',
filter: 'id=eq.1',
limit: 1,
);
final instance = Demo(age: 1, name: 'Demo 1', id: '1');
final resp = SupabaseResponse(await mock.serialize(instance));
mock.handle({req: resp});

final provider = SupabaseProvider(mock.client, modelDictionary: supabaseModelDictionary);
final inserted = await provider.upsert<Demo>(
instance,
query: const Query(
forProviders: [SupabaseProviderQuery(upsertMethod: UpsertMethod.update)],
),
);
expect(inserted.id, instance.id);
expect(inserted.age, instance.age);
expect(inserted.name, instance.name);
});
});
});
}

0 comments on commit 9a41b95

Please sign in to comment.