Skip to content

Commit 0072ec2

Browse files
feat: Sql support (#29)
Co-authored-by: Tim Holm <[email protected]>
1 parent 6529881 commit 0072ec2

35 files changed

+1015
-219
lines changed

example/services/nitric_example.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import 'package:nitric_sdk/nitric.dart';
2-
import 'package:nitric_sdk/resources.dart';
32
import 'package:uuid/uuid.dart';
43

54
class Profile {

lib/src/api/api.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
import 'package:grpc/grpc.dart';
2+
13
export 'bucket.dart';
24
export 'keyvalue.dart';
35
export 'secret.dart';
46
export 'topic.dart';
57
export 'proto.dart';
68
export 'queue.dart';
9+
10+
typedef UseClientCallback<T extends Client, Resp> = Future<Resp> Function(T);

lib/src/api/bucket.dart

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:async';
22
import 'dart:convert';
33

4+
import 'package:nitric_sdk/src/api/api.dart';
45
import 'package:nitric_sdk/src/context/common.dart';
56
import 'package:nitric_sdk/src/grpc_helper.dart';
67
import 'package:nitric_sdk/src/nitric/proto/storage/v1/storage.pbgrpc.dart'
@@ -10,21 +11,15 @@ import 'package:fixnum/fixnum.dart';
1011
import 'package:nitric_sdk/src/workers/common.dart';
1112

1213
class Bucket {
13-
late $p.StorageClient _storageClient;
14-
late $p.StorageListenerClient? _storageListenerClient;
14+
late final $p.StorageClient? _storageClient;
15+
late final $p.StorageListenerClient? _storageListenerClient;
1516

1617
String name;
1718

1819
Bucket(this.name,
19-
{$p.StorageClient? client,
20-
$p.StorageListenerClient? storageListenerClient}) {
21-
if (client == null) {
22-
_storageClient =
23-
$p.StorageClient(ClientChannelSingleton.instance.clientChannel);
24-
} else {
25-
_storageClient = client;
26-
}
27-
20+
{$p.StorageListenerClient? storageListenerClient,
21+
$p.StorageClient? client}) {
22+
_storageClient = client;
2823
_storageListenerClient = storageListenerClient;
2924
}
3025

@@ -33,12 +28,27 @@ class Bucket {
3328
return File(this, key);
3429
}
3530

31+
Future<Resp> _useClient<Resp>(
32+
UseClientCallback<$p.StorageClient, Resp> callback) async {
33+
final client = _storageClient ??
34+
$p.StorageClient(ClientChannelSingleton.instance.clientChannel);
35+
36+
var resp = await callback(client);
37+
38+
if (_storageClient == null) {
39+
await ClientChannelSingleton.instance.release();
40+
}
41+
42+
return resp;
43+
}
44+
3645
/// Get a list of references to the files in the bucket. Optionally supply a [prefix] to filter by.
3746
Future<List<File>> files({String prefix = ""}) async {
3847
final request =
3948
$p.StorageListBlobsRequest(bucketName: name, prefix: prefix);
4049

41-
var resp = await _storageClient.listBlobs(request);
50+
var resp =
51+
await _useClient((client) async => await client.listBlobs(request));
4252

4353
return resp.blobs.map((blob) => File(this, blob.key)).toList();
4454
}
@@ -81,7 +91,7 @@ class File {
8191
key: key,
8292
);
8393

84-
await _bucket._storageClient.delete(req);
94+
await _bucket._useClient((client) async => await client.delete(req));
8595
}
8696

8797
/// Read the file from the bucket.
@@ -91,7 +101,8 @@ class File {
91101
key: key,
92102
);
93103

94-
var resp = await _bucket._storageClient.read(req);
104+
var resp =
105+
await _bucket._useClient((client) async => await client.read(req));
95106

96107
return utf8.decode(resp.body);
97108
}
@@ -106,7 +117,7 @@ class File {
106117
body: bytes,
107118
);
108119

109-
await _bucket._storageClient.write(req);
120+
await _bucket._useClient((client) async => await client.write(req));
110121
}
111122

112123
/// Check whether the file exists in the bucket.
@@ -116,7 +127,8 @@ class File {
116127
key: key,
117128
);
118129

119-
var resp = await _bucket._storageClient.exists(req);
130+
var resp =
131+
await _bucket._useClient((client) async => await client.exists(req));
120132

121133
return resp.exists;
122134
}
@@ -148,7 +160,8 @@ class File {
148160
expiry: exp,
149161
);
150162

151-
var resp = await _bucket._storageClient.preSignUrl(req);
163+
var resp = await _bucket
164+
._useClient((client) async => await client.preSignUrl(req));
152165

153166
return resp.url;
154167
}

lib/src/api/keyvalue.dart

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,40 @@
1+
import 'dart:async';
2+
13
import 'package:nitric_sdk/src/api/api.dart';
24
import 'package:nitric_sdk/src/grpc_helper.dart';
35
import 'package:nitric_sdk/src/nitric/proto/kvstore/v1/kvstore.pbgrpc.dart'
46
as $p;
57

68
/// A Key Value Store.
79
class KeyValueStore {
8-
late $p.KvStoreClient _keyValueClient;
10+
late final $p.KvStoreClient? _keyValueClient;
911

1012
final String name;
1113

1214
KeyValueStore(this.name, {$p.KvStoreClient? client}) {
13-
if (client == null) {
14-
_keyValueClient =
15-
$p.KvStoreClient(ClientChannelSingleton.instance.clientChannel);
16-
} else {
17-
_keyValueClient = client;
15+
_keyValueClient = client;
16+
}
17+
18+
Future<Resp> _useClient<Resp>(
19+
UseClientCallback<$p.KvStoreClient, Resp> callback) async {
20+
final client = _keyValueClient ??
21+
$p.KvStoreClient(ClientChannelSingleton.instance.clientChannel);
22+
23+
var resp = callback(client);
24+
25+
if (_keyValueClient == null) {
26+
await ClientChannelSingleton.instance.release();
1827
}
28+
29+
return resp;
1930
}
2031

2132
/// Get a reference to a [key] in the store.
2233
Future<Map<String, dynamic>> get(String key) async {
2334
var req =
2435
$p.KvStoreGetValueRequest(ref: $p.ValueRef(key: key, store: name));
2536

26-
var resp = await _keyValueClient.getValue(req);
37+
var resp = await _useClient((client) async => await client.getValue(req));
2738

2839
return Proto.mapFromStruct(resp.value.content);
2940
}
@@ -35,23 +46,23 @@ class KeyValueStore {
3546
var req = $p.KvStoreSetValueRequest(
3647
ref: $p.ValueRef(key: key, store: name), content: content);
3748

38-
await _keyValueClient.setValue(req);
49+
await _useClient((client) async => await client.setValue(req));
3950
}
4051

4152
/// Delete a [key] from the store.
4253
Future<void> delete(String key) async {
4354
var req =
4455
$p.KvStoreDeleteKeyRequest(ref: $p.ValueRef(key: key, store: name));
4556

46-
await _keyValueClient.deleteKey(req);
57+
await _useClient((client) async => await client.deleteKey(req));
4758
}
4859

4960
/// Get a stream of key values that match the [prefix].
50-
Stream<String> keys({String prefix = ""}) {
61+
Future<Stream<String>> keys({String prefix = ""}) async {
5162
var req =
5263
$p.KvStoreScanKeysRequest(store: $p.Store(name: name), prefix: prefix);
5364

54-
var resp = _keyValueClient.scanKeys(req);
65+
var resp = await _useClient((client) async => client.scanKeys(req));
5566

5667
return resp.map((event) => event.key);
5768
}

lib/src/api/queue.dart

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,28 @@ import 'package:nitric_sdk/src/grpc_helper.dart';
55
import 'package:nitric_sdk/src/nitric/proto/queues/v1/queues.pbgrpc.dart' as $p;
66

77
class Queue {
8-
late $p.QueuesClient _queuesClient;
8+
late final $p.QueuesClient? _queuesClient;
99

1010
/// The name of the queue.
1111
String name;
1212

1313
/// Construct a new queue.
1414
Queue(this.name, {$p.QueuesClient? client}) {
15-
if (client == null) {
16-
_queuesClient =
17-
$p.QueuesClient(ClientChannelSingleton.instance.clientChannel);
18-
} else {
19-
_queuesClient = client;
15+
_queuesClient = client;
16+
}
17+
18+
Future<Resp> _useClient<Resp>(
19+
UseClientCallback<$p.QueuesClient, Resp> callback) async {
20+
final client = _queuesClient ??
21+
$p.QueuesClient(ClientChannelSingleton.instance.clientChannel);
22+
23+
var resp = callback(client);
24+
25+
if (_queuesClient == null) {
26+
await ClientChannelSingleton.instance.release();
2027
}
28+
29+
return resp;
2130
}
2231

2332
/// Enqueue a list of [messages] to the queue.
@@ -31,7 +40,7 @@ class Queue {
3140
queueName: name,
3241
);
3342

34-
var resp = await _queuesClient.enqueue(req);
43+
var resp = await _useClient((client) async => await client.enqueue(req));
3544

3645
return resp.failedMessages.map((fm) => FailedMessage(fm)).toList();
3746
}
@@ -40,7 +49,7 @@ class Queue {
4049
Future<List<DequeuedMessage>> dequeue({int depth = 1}) async {
4150
var req = $p.QueueDequeueRequest(queueName: name, depth: depth);
4251

43-
var resp = await _queuesClient.dequeue(req);
52+
var resp = await _useClient((client) async => await client.dequeue(req));
4453

4554
return resp.messages.map((m) => DequeuedMessage(this, m)).toList();
4655
}
@@ -64,7 +73,7 @@ class DequeuedMessage {
6473
var req =
6574
$p.QueueCompleteRequest(leaseId: _leaseId, queueName: _queue.name);
6675

67-
await _queue._queuesClient.complete(req);
76+
await _queue._useClient((client) async => await client.complete(req));
6877
}
6978
}
7079

lib/src/api/secret.dart

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,30 @@ import 'package:nitric_sdk/src/grpc_helper.dart';
44
import 'package:nitric_sdk/src/nitric/proto/secrets/v1/secrets.pbgrpc.dart'
55
as $p;
66

7+
import 'api.dart';
8+
79
/// References an encrypted secret stored in a secret manager.
810
class Secret {
911
/// The name of the secret
1012
final String name;
11-
late final $p.SecretManagerClient _secretClient;
13+
late final $p.SecretManagerClient? _secretClient;
1214

1315
Secret(this.name, {$p.SecretManagerClient? client}) {
14-
if (client == null) {
15-
_secretClient =
16-
$p.SecretManagerClient(ClientChannelSingleton.instance.clientChannel);
17-
} else {
18-
_secretClient = client;
16+
_secretClient = client;
17+
}
18+
19+
Future<Resp> _useClient<Resp>(
20+
UseClientCallback<$p.SecretManagerClient, Resp> callback) async {
21+
final client = _secretClient ??
22+
$p.SecretManagerClient(ClientChannelSingleton.instance.clientChannel);
23+
24+
var resp = callback(client);
25+
26+
if (_secretClient == null) {
27+
await ClientChannelSingleton.instance.release();
1928
}
29+
30+
return resp;
2031
}
2132

2233
/// Get a reference to a specific [version] of this secret.
@@ -32,7 +43,7 @@ class Secret {
3243
/// Put a new [value] to the secret, creating a new secret version and returning it.
3344
Future<SecretVersion> put(String value) async {
3445
var req = $p.SecretPutRequest(secret: _toWire(), value: utf8.encode(value));
35-
var resp = await _secretClient.put(req);
46+
var resp = await _useClient((client) async => client.put(req));
3647

3748
return SecretVersion._fromWire(this, resp.secretVersion);
3849
}
@@ -60,7 +71,8 @@ class SecretVersion {
6071
/// Access the value of this secret version.
6172
Future<SecretValue> access() async {
6273
var req = $p.SecretAccessRequest(secretVersion: _toWire());
63-
var resp = await _secret._secretClient.access(req);
74+
var resp =
75+
await _secret._useClient((client) async => await client.access(req));
6476

6577
return SecretValue(version, utf8.decode(resp.value));
6678
}

lib/src/api/sql.dart

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import 'package:nitric_sdk/nitric.dart';
2+
import 'package:nitric_sdk/src/grpc_helper.dart';
3+
import 'package:nitric_sdk/src/nitric/proto/resources/v1/resources.pb.dart';
4+
import 'package:nitric_sdk/src/nitric/proto/resources/v1/resources.pbgrpc.dart'
5+
as $rp;
6+
import 'package:nitric_sdk/src/nitric/proto/sql/v1/sql.pbgrpc.dart' as $p;
7+
8+
import 'api.dart';
9+
10+
/// A Topic for publishing events to subscribers of this topic.
11+
class SqlDatabase extends Resource {
12+
/// The name of the topic
13+
late final String? migrations;
14+
late final $p.SqlClient? _sqlClient;
15+
16+
SqlDatabase(String name,
17+
{this.migrations,
18+
$p.SqlClient? client,
19+
$rp.ResourcesClient? resourcesClient})
20+
: super(name, resourcesClient) {
21+
_sqlClient = client;
22+
}
23+
24+
Future<Resp> _useClient<Resp>(
25+
UseClientCallback<$p.SqlClient, Resp> callback) async {
26+
final client = _sqlClient ??
27+
$p.SqlClient(ClientChannelSingleton.instance.clientChannel);
28+
29+
var resp = callback(client);
30+
31+
if (_sqlClient == null) {
32+
await ClientChannelSingleton.instance.release();
33+
}
34+
35+
return resp;
36+
}
37+
38+
/// Returns a connection endpoint to connect to the SQL database
39+
Future<String> connectionString() async {
40+
final req = $p.SqlConnectionStringRequest(databaseName: name);
41+
42+
final resp =
43+
await _useClient((client) async => await client.connectionString(req));
44+
45+
return resp.connectionString;
46+
}
47+
48+
@override
49+
$rp.ResourceDeclareRequest asRequest() {
50+
var resource =
51+
$rp.ResourceIdentifier(name: name, type: $rp.ResourceType.SqlDatabase);
52+
53+
return $rp.ResourceDeclareRequest(
54+
id: resource,
55+
sqlDatabase: $rp.SqlDatabaseResource(
56+
migrations: SqlDatabaseMigrations(migrationsPath: migrations)));
57+
}
58+
}

0 commit comments

Comments
 (0)