Skip to content
This repository was archived by the owner on May 13, 2023. It is now read-only.

Commit 35d070a

Browse files
feat: types (#94)
* feat: types * test: style * test: migrate tests to new type format * fix: convert Lists properly * docs: update README * feat: type insert/update/delete void * docs: update readme * feat: assert correct generic * fix: remove outdated comment from `from` * style: remove unused typedef * test: cleaner test * refactor: improve docs * fix: add List<Map> as allowed type * Update lib/src/postgrest_query_builder.dart * Update lib/src/postgrest_transform_builder.dart Co-authored-by: Tyler <18113850+dshukertjr@users.noreply.github.com> * fix: support PostgrestResponse<Map<String,dynamic>?> * refactor: improve doc Co-authored-by: Tyler <18113850+dshukertjr@users.noreply.github.com>
1 parent 3612c37 commit 35d070a

12 files changed

+485
-315
lines changed

README.md

+18-5
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,20 @@ import 'package:postgrest/postgrest.dart';
2222
2323
final url = 'https://example.com/postgrest/endpoint';
2424
final client = PostgrestClient(url);
25-
final response = await client.from('users').select();
25+
final response = await client.from<PostgrestList>('users').select();
26+
```
27+
28+
#### Reading your data and converting it to an object
29+
30+
```dart
31+
import 'package:postgrest/postgrest.dart';
32+
33+
final url = 'https://example.com/postgrest/endpoint';
34+
final client = PostgrestClient(url);
35+
final response = await client
36+
.from('users')
37+
.select<PostgrestList>()
38+
.withConverter((data) => data.map(User.fromJson).toList());
2639
```
2740

2841
#### Insert records
@@ -33,7 +46,7 @@ import 'package:postgrest/postgrest.dart';
3346
final url = 'https://example.com/postgrest/endpoint';
3447
final client = PostgrestClient(url);
3548
try {
36-
final data = await client.from('users')
49+
await client.from('users')
3750
.insert([
3851
{'username': 'supabot', 'status': 'ONLINE'}
3952
]);
@@ -53,7 +66,7 @@ import 'package:postgrest/postgrest.dart';
5366
5467
final url = 'https://example.com/postgrest/endpoint';
5568
final client = PostgrestClient(url);
56-
final data = await client.from('users')
69+
await client.from('users')
5770
.update({'status': 'OFFLINE'})
5871
.eq('username', 'dragarcia');
5972
```
@@ -65,7 +78,7 @@ import 'package:postgrest/postgrest.dart';
6578
6679
final url = 'https://example.com/postgrest/endpoint';
6780
final client = PostgrestClient(url);
68-
final data = await client.from('users')
81+
await client.from('users')
6982
.delete()
7083
.eq('username', 'supabot');
7184
```
@@ -78,7 +91,7 @@ import 'package:postgrest/postgrest.dart';
7891
final url = 'https://example.com/postgrest/endpoint';
7992
final client = PostgrestClient(url);
8093
final response = await client.from('countries')
81-
.select('*', FetchOptions(count: CountOption.exact));
94+
.select<PostgrestResponse>('*', FetchOptions(count: CountOption.exact));
8295
final data = response.data;
8396
final count = response.count;
8497
```

lib/src/postgrest.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ class PostgrestClient {
3232
}
3333

3434
/// Perform a table operation.
35-
PostgrestQueryBuilder from(String table) {
35+
PostgrestQueryBuilder<void> from(String table) {
3636
final url = '${this.url}/$table';
37-
return PostgrestQueryBuilder(
37+
return PostgrestQueryBuilder<void>(
3838
url,
3939
headers: headers,
4040
schema: schema,

lib/src/postgrest_builder.dart

+81-14
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,17 @@ const METHOD_PUT = 'PUT';
2323
const METHOD_PATCH = 'PATCH';
2424
const METHOD_DELETE = 'DELETE';
2525

26+
typedef _Nullable<T> = T?;
27+
2628
/// The base builder class.
27-
class PostgrestBuilder<T> implements Future<T?> {
29+
class PostgrestBuilder<T, S> implements Future<T> {
2830
dynamic _body;
2931
late final Headers _headers;
3032
bool _maybeEmpty = false;
3133
String? _method;
3234
late final String? _schema;
3335
late Uri _url;
34-
PostgrestConverter? _converter;
36+
PostgrestConverter<T, S>? _converter;
3537
late final Client? _httpClient;
3638
// ignore: prefer_final_fields
3739
FetchOptions? _options;
@@ -62,9 +64,8 @@ class PostgrestBuilder<T> implements Future<T?> {
6264
/// .select()
6365
/// .withConverter<User>((data) => User.fromJson(data));
6466
/// ```
65-
PostgrestBuilder<S> withConverter<S>(PostgrestConverter<S> converter) {
66-
_converter = converter;
67-
return PostgrestBuilder<S>(
67+
PostgrestBuilder<R, T> withConverter<R>(PostgrestConverter<R, T> converter) {
68+
return PostgrestBuilder<R, T>(
6869
url: _url,
6970
headers: _headers,
7071
schema: _schema,
@@ -75,6 +76,22 @@ class PostgrestBuilder<T> implements Future<T?> {
7576
.._converter = converter;
7677
}
7778

79+
void _assertCorrectGeneric(Type R) {
80+
assert(
81+
R == PostgrestList ||
82+
R == PostgrestMap ||
83+
R == (_Nullable<PostgrestMap>) ||
84+
R == PostgrestListResponse ||
85+
R == PostgrestMapResponse ||
86+
R == (PostgrestResponse<PostgrestMap?>) ||
87+
R == PostgrestResponse ||
88+
R == List ||
89+
R == (List<Map>) ||
90+
R == Map ||
91+
R == dynamic,
92+
"$R is not allowed as generic for `select<R>()`. Allowed types are: `PostgrestList`, `PostgrestMap`, `PostgrestMap?`, `PostgrestListResponse`, `PostgrestMapResponse`, `PostgrestResponse`, `dynamic`.");
93+
}
94+
7895
/// Sends the request and returns a [PostgrestResponse]
7996
///
8097
/// [head] to trigger a HEAD request
@@ -92,7 +109,7 @@ class PostgrestBuilder<T> implements Future<T?> {
92109
/// }
93110
/// ```
94111
@Deprecated('Use async/await or .then instead. Deprecated in 0.2.0')
95-
Future<PostgrestResponse<T>> execute({
112+
Future<PostgrestResponse> execute({
96113
bool head = false,
97114
CountOption? count,
98115
}) async {
@@ -103,7 +120,7 @@ class PostgrestBuilder<T> implements Future<T?> {
103120
return _execute();
104121
}
105122

106-
Future<PostgrestResponse<T>> _execute() async {
123+
Future<PostgrestResponse> _execute() async {
107124
if (_options?.head ?? false) {
108125
_method = METHOD_HEAD;
109126
}
@@ -183,7 +200,7 @@ class PostgrestBuilder<T> implements Future<T?> {
183200
}
184201

185202
/// Parse request response to json object if possible
186-
Future<PostgrestResponse<T>> _parseResponse(http.Response response) async {
203+
Future<PostgrestResponse> _parseResponse(http.Response response) async {
187204
if (response.statusCode >= 200 && response.statusCode <= 299) {
188205
dynamic body;
189206
int? count;
@@ -207,12 +224,62 @@ class PostgrestBuilder<T> implements Future<T?> {
207224
: int.parse(contentRange.split('/').last);
208225
}
209226

227+
// When using converter [S] is the type of the converter functions's argument. Otherwise [T] should be equal to [S]
228+
if (S == PostgrestList) {
229+
body = PostgrestList.from(body as Iterable) as S;
230+
} else if (S == List<Map>) {
231+
body = List<Map>.from(body as Iterable) as S;
232+
} else if (S == PostgrestMap) {
233+
body = PostgrestMap.from(body as Map) as S;
234+
235+
//You can't write `S == PostgrestMap?`
236+
} else if (S == _Nullable<PostgrestMap>) {
237+
if (body == null) {
238+
body = null as S;
239+
} else {
240+
body = PostgrestMap.from(body as Map) as S;
241+
}
242+
} else if (S == PostgrestListResponse) {
243+
body = PostgrestList.from(body as Iterable);
244+
if (_converter != null) {
245+
body = _converter!(body as S);
246+
}
247+
return PostgrestResponse<PostgrestList>(
248+
data: body,
249+
status: response.statusCode,
250+
count: count,
251+
);
252+
} else if (S == PostgrestMapResponse) {
253+
body = PostgrestMap.from(body as Map);
254+
if (_converter != null) {
255+
body = _converter!(body as S);
256+
}
257+
return PostgrestResponse<PostgrestMap>(
258+
data: body,
259+
status: response.statusCode,
260+
count: count,
261+
);
262+
} else if (S == PostgrestResponse<PostgrestMap?>) {
263+
if (body == null) {
264+
body = null;
265+
} else {
266+
body = PostgrestMap.from(body as Map);
267+
}
268+
if (_converter != null) {
269+
body = _converter!(body as S);
270+
}
271+
return PostgrestResponse<PostgrestMap?>(
272+
data: body,
273+
status: response.statusCode,
274+
count: count,
275+
);
276+
}
210277
if (_converter != null) {
211278
body = _converter!(body);
212279
}
213280

214-
return PostgrestResponse<T>(
215-
data: body as T,
281+
return PostgrestResponse(
282+
data: body,
216283
status: response.statusCode,
217284
count: count,
218285
);
@@ -289,8 +356,8 @@ class PostgrestBuilder<T> implements Future<T?> {
289356
}
290357

291358
@override
292-
Stream<T?> asStream() {
293-
final controller = StreamController<T?>.broadcast();
359+
Stream<T> asStream() {
360+
final controller = StreamController<T>.broadcast();
294361

295362
then((value) {
296363
controller.add(value);
@@ -311,7 +378,7 @@ class PostgrestBuilder<T> implements Future<T?> {
311378
/// Register callbacks to be called when this future completes.
312379
@override
313380
Future<R> then<R>(
314-
FutureOr<R> Function(T? value) onValue, {
381+
FutureOr<R> Function(T value) onValue, {
315382
Function? onError,
316383
}) async {
317384
if (onError != null &&
@@ -378,7 +445,7 @@ class PostgrestBuilder<T> implements Future<T?> {
378445
}
379446

380447
@override
381-
Future<T?> whenComplete(FutureOr<void> Function() action) {
448+
Future<T> whenComplete(FutureOr<void> Function() action) {
382449
return then(
383450
(v) {
384451
final f2 = action();

0 commit comments

Comments
 (0)