mirror of
https://gitlab.com/veilid/veilid.git
synced 2024-10-01 01:26:08 -04:00
flutter unit/integration tests
This commit is contained in:
parent
d586748333
commit
6a8c0830d2
20
Cargo.lock
generated
20
Cargo.lock
generated
@ -6172,9 +6172,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.91"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
|
||||
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"serde",
|
||||
@ -6184,9 +6184,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.91"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
|
||||
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
@ -6211,9 +6211,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.91"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
|
||||
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@ -6221,9 +6221,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.91"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
|
||||
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -6234,9 +6234,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.91"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
|
||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
|
4
veilid-flutter/debug_integration_tests.sh
Executable file
4
veilid-flutter/debug_integration_tests.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
pushd example 2>/dev/null
|
||||
flutter run integration_test/app_test.dart $@
|
||||
popd 2>/dev/null
|
@ -2,39 +2,72 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'fixtures.dart';
|
||||
import 'test_crypto.dart';
|
||||
import 'test_routing_context.dart';
|
||||
import 'test_table_db.dart';
|
||||
import 'test_veilid_config.dart';
|
||||
import 'test_dht.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
final fixture = DefaultFixture();
|
||||
|
||||
group('VeilidConfig', () {
|
||||
final fixture = DefaultFixture();
|
||||
setUp(fixture.setUp);
|
||||
tearDown(fixture.tearDown);
|
||||
|
||||
test('test VeilidConfig defaults', testVeilidConfigDefaults);
|
||||
group('Unstarted Tests', () {
|
||||
test('veilid config defaults', testVeilidConfigDefaults);
|
||||
});
|
||||
|
||||
// group('end-to-end test', () {
|
||||
// testWidgets('tap on the floating action button, verify counter',
|
||||
// (tester) async {
|
||||
// // Load app widget.
|
||||
// await tester.pumpWidget(const MyApp());
|
||||
group('Started Tests', () {
|
||||
setUpAll(fixture.setUp);
|
||||
tearDownAll(fixture.tearDown);
|
||||
|
||||
// // Verify the counter starts at 0.
|
||||
// expect(find.text('0'), findsOneWidget);
|
||||
group('Crypto Tests', () {
|
||||
test('best cryptosystem', testBestCryptoSystem);
|
||||
test('get cryptosystem', testGetCryptoSystem);
|
||||
test('get cryptosystem invalid', testGetCryptoSystemInvalid);
|
||||
test('hash and verify password', testHashAndVerifyPassword);
|
||||
});
|
||||
|
||||
// // Finds the floating action button to tap on.
|
||||
// final fab = find.byKey(const Key('increment'));
|
||||
group('Table DB Tests', () {
|
||||
test('delete table db nonexistent', testDeleteTableDbNonExistent);
|
||||
test('open delete table db', testOpenDeleteTableDb);
|
||||
test('open twice table db', testOpenTwiceTableDb);
|
||||
test('open twice table db store load', testOpenTwiceTableDbStoreLoad);
|
||||
test('open twice table db store delete load',
|
||||
testOpenTwiceTableDbStoreDeleteLoad);
|
||||
test('resize table db', testResizeTableDb);
|
||||
});
|
||||
|
||||
// // Emulate a tap on the floating action button.
|
||||
// await tester.tap(fab);
|
||||
group('Attached Tests', () {
|
||||
setUpAll(fixture.attach);
|
||||
tearDownAll(fixture.detach);
|
||||
|
||||
// // Trigger a frame.
|
||||
// await tester.pumpAndSettle();
|
||||
group('Routing Contexts', () {
|
||||
test('routing contexts', testRoutingContexts);
|
||||
test('app message loopback',
|
||||
() => testAppMessageLoopback(fixture.updateStream));
|
||||
test('app call loopback',
|
||||
() => testAppCallLoopback(fixture.updateStream));
|
||||
test('app message loopback big packets',
|
||||
() => testAppMessageLoopbackBigPackets(fixture.updateStream));
|
||||
test('app call loopback big packets',
|
||||
() => testAppCallLoopbackBigPackets(fixture.updateStream));
|
||||
});
|
||||
|
||||
// // Verify the counter increments by 1.
|
||||
// expect(find.text('1'), findsOneWidget);
|
||||
// });
|
||||
// });
|
||||
group('Veilid DHT', () {
|
||||
test('get dht value unopened', testGetDHTValueUnopened);
|
||||
test('open dht record nonexistent no writer',
|
||||
testOpenDHTRecordNonexistentNoWriter);
|
||||
test('close dht record nonexistent', testCloseDHTRecordNonexistent);
|
||||
test('delete dht record nonexistent', testDeleteDHTRecordNonexistent);
|
||||
test(
|
||||
'create delete dht record simple', testCreateDeleteDHTRecordSimple);
|
||||
test('create delete dht record no close',
|
||||
testCreateDeleteDHTRecordNoClose);
|
||||
test('get dht value nonexistent', testGetDHTValueNonexistent);
|
||||
test('set get dht value', testSetGetDHTValue);
|
||||
test('open writer dht value', testOpenWriterDHTValue);
|
||||
test('inspect dht record', testInspectDHTRecord);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -7,15 +7,17 @@ import 'package:veilid/veilid.dart';
|
||||
class DefaultFixture {
|
||||
DefaultFixture();
|
||||
|
||||
StreamSubscription<VeilidUpdate>? _updateSubscription;
|
||||
Stream<VeilidUpdate>? _updateStream;
|
||||
StreamSubscription<VeilidUpdate>? _veilidUpdateSubscription;
|
||||
Stream<VeilidUpdate>? _veilidUpdateStream;
|
||||
final StreamController<VeilidUpdate> _updateStreamController =
|
||||
StreamController.broadcast();
|
||||
|
||||
static final _fixtureMutex = Mutex();
|
||||
|
||||
Future<void> setUp() async {
|
||||
await _fixtureMutex.acquire();
|
||||
|
||||
assert(_updateStream == null, 'should not set up fixture twice');
|
||||
assert(_veilidUpdateStream == null, 'should not set up fixture twice');
|
||||
|
||||
final Map<String, dynamic> platformConfigJson;
|
||||
if (kIsWeb) {
|
||||
@ -53,16 +55,26 @@ class DefaultFixture {
|
||||
}
|
||||
Veilid.instance.initializeVeilidCore(platformConfigJson);
|
||||
|
||||
final defaultConfig = await getDefaultVeilidConfig(
|
||||
isWeb: kIsWeb, programName: 'Veilid Tests');
|
||||
var config = await getDefaultVeilidConfig(
|
||||
isWeb: kIsWeb,
|
||||
programName: 'Veilid Tests',
|
||||
// ignore: avoid_redundant_argument_values, do_not_use_environment
|
||||
bootstrap: const String.fromEnvironment('BOOTSTRAP'),
|
||||
// ignore: avoid_redundant_argument_values, do_not_use_environment
|
||||
networkKeyPassword: const String.fromEnvironment('NETWORK_KEY'),
|
||||
);
|
||||
|
||||
final updateStream =
|
||||
_updateStream = await Veilid.instance.startupVeilidCore(defaultConfig);
|
||||
if (_updateStream == null) {
|
||||
throw Exception('failed to start up veilid core');
|
||||
}
|
||||
config =
|
||||
config.copyWith(tableStore: config.tableStore.copyWith(delete: true));
|
||||
config = config.copyWith(
|
||||
protectedStore: config.protectedStore.copyWith(delete: true));
|
||||
config =
|
||||
config.copyWith(blockStore: config.blockStore.copyWith(delete: true));
|
||||
|
||||
_updateSubscription = updateStream.listen((update) {
|
||||
final us =
|
||||
_veilidUpdateStream = await Veilid.instance.startupVeilidCore(config);
|
||||
|
||||
_veilidUpdateSubscription = us.listen((update) {
|
||||
if (update is VeilidLog) {
|
||||
} else if (update is VeilidUpdateAttachment) {
|
||||
} else if (update is VeilidUpdateConfig) {
|
||||
@ -70,21 +82,56 @@ class DefaultFixture {
|
||||
} else if (update is VeilidAppMessage) {
|
||||
} else if (update is VeilidAppCall) {
|
||||
} else if (update is VeilidUpdateValueChange) {
|
||||
} else if (update is VeilidUpdateRouteChange) {
|
||||
} else {
|
||||
throw Exception('unexpected update: $update');
|
||||
}
|
||||
_updateStreamController.sink.add(update);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> tearDown() async {
|
||||
assert(_updateStream != null, 'should not tearDown without setUp');
|
||||
Stream<VeilidUpdate> get updateStream => _updateStreamController.stream;
|
||||
|
||||
final cancelFut = _updateSubscription?.cancel();
|
||||
Future<void> attach() async {
|
||||
await Veilid.instance.attach();
|
||||
|
||||
// Wait for attached state
|
||||
while (true) {
|
||||
final state = await Veilid.instance.getVeilidState();
|
||||
var done = false;
|
||||
if (state.attachment.publicInternetReady) {
|
||||
switch (state.attachment.state) {
|
||||
case AttachmentState.detached:
|
||||
break;
|
||||
case AttachmentState.attaching:
|
||||
break;
|
||||
case AttachmentState.detaching:
|
||||
break;
|
||||
default:
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> detach() async {
|
||||
await Veilid.instance.detach();
|
||||
}
|
||||
|
||||
Future<void> tearDown() async {
|
||||
assert(_veilidUpdateStream != null, 'should not tearDown without setUp');
|
||||
|
||||
final cancelFut = _veilidUpdateSubscription?.cancel();
|
||||
await Veilid.instance.shutdownVeilidCore();
|
||||
await cancelFut;
|
||||
|
||||
_updateSubscription = null;
|
||||
_updateStream = null;
|
||||
_veilidUpdateSubscription = null;
|
||||
_veilidUpdateStream = null;
|
||||
|
||||
_fixtureMutex.release();
|
||||
}
|
||||
|
34
veilid-flutter/example/integration_test/test_crypto.dart
Normal file
34
veilid-flutter/example/integration_test/test_crypto.dart
Normal file
@ -0,0 +1,34 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:veilid/veilid.dart';
|
||||
|
||||
Future<void> testBestCryptoSystem() async {
|
||||
final cs = await Veilid.instance.bestCryptoSystem();
|
||||
expect(await cs.defaultSaltLength(), equals(16));
|
||||
}
|
||||
|
||||
Future<void> testGetCryptoSystem() async {
|
||||
final cs = await Veilid.instance.getCryptoSystem(cryptoKindVLD0);
|
||||
expect(await cs.defaultSaltLength(), equals(16));
|
||||
}
|
||||
|
||||
Future<void> testGetCryptoSystemInvalid() async {
|
||||
await expectLater(
|
||||
() async => await Veilid.instance.getCryptoSystem(cryptoKindNONE),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
}
|
||||
|
||||
Future<void> testHashAndVerifyPassword() async {
|
||||
final cs = await Veilid.instance.bestCryptoSystem();
|
||||
final nonce = await cs.randomNonce();
|
||||
final salt = nonce.decode();
|
||||
|
||||
// Password match
|
||||
final phash = await cs.hashPassword(utf8.encode("abc123"), salt);
|
||||
expect(await cs.verifyPassword(utf8.encode("abc123"), phash), isTrue);
|
||||
|
||||
// Password mismatch
|
||||
await cs.hashPassword(utf8.encode("abc1234"), salt);
|
||||
expect(await cs.verifyPassword(utf8.encode("abc1235"), phash), isFalse);
|
||||
}
|
250
veilid-flutter/example/integration_test/test_dht.dart
Normal file
250
veilid-flutter/example/integration_test/test_dht.dart
Normal file
@ -0,0 +1,250 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:veilid/veilid.dart';
|
||||
|
||||
final bogusKey =
|
||||
TypedKey.fromString("VLD0:qD10lHHPD1_Qr23_Qy-1JnxTht12eaWwENVG_m2v7II");
|
||||
|
||||
Future<void> testGetDHTValueUnopened() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
await expectLater(
|
||||
() async => await rc.getDHTValue(bogusKey, 0, forceRefresh: false),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testOpenDHTRecordNonexistentNoWriter() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
await expectLater(() async => await rc.openDHTRecord(bogusKey),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testCloseDHTRecordNonexistent() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
await expectLater(() async => await rc.closeDHTRecord(bogusKey),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testDeleteDHTRecordNonexistent() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
await expectLater(() async => await rc.deleteDHTRecord(bogusKey),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testCreateDeleteDHTRecordSimple() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
final rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 1));
|
||||
await rc.closeDHTRecord(rec.key);
|
||||
await rc.deleteDHTRecord(rec.key);
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testCreateDeleteDHTRecordNoClose() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
final rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 1));
|
||||
await rc.deleteDHTRecord(rec.key);
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testGetDHTValueNonexistent() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
final rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 1));
|
||||
expect(await rc.getDHTValue(rec.key, 0), isNull);
|
||||
await rc.deleteDHTRecord(rec.key);
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testSetGetDHTValue() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
final rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 2));
|
||||
expect(await rc.setDHTValue(rec.key, 0, utf8.encode("BLAH BLAH BLAH")),
|
||||
isNull);
|
||||
final vd2 = await rc.getDHTValue(rec.key, 0);
|
||||
expect(vd2, isNotNull);
|
||||
|
||||
final vd3 = await rc.getDHTValue(rec.key, 0, forceRefresh: true);
|
||||
expect(vd3, isNotNull);
|
||||
|
||||
final vd4 = await rc.getDHTValue(rec.key, 1);
|
||||
expect(vd4, isNull);
|
||||
|
||||
expect(vd2, equals(vd3));
|
||||
|
||||
await rc.deleteDHTRecord(rec.key);
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testOpenWriterDHTValue() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
var rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 2));
|
||||
final key = rec.key;
|
||||
final owner = rec.owner;
|
||||
final secret = rec.ownerSecret!;
|
||||
|
||||
final cs = await Veilid.instance.getCryptoSystem(rec.key.kind);
|
||||
expect(await cs.validateKeyPair(owner, secret), isTrue);
|
||||
final otherKeyPair = await cs.generateKeyPair();
|
||||
|
||||
final va = utf8.encode("Qwertyuiop Asdfghjkl Zxcvbnm");
|
||||
final vb = utf8.encode("1234567890");
|
||||
final vc = utf8.encode("!@#\$%^&*()");
|
||||
|
||||
// Test subkey writes
|
||||
expect(await rc.setDHTValue(key, 1, va), isNull);
|
||||
|
||||
var vdtemp = await rc.getDHTValue(key, 1);
|
||||
expect(vdtemp, isNotNull);
|
||||
expect(vdtemp!.data, equals(va));
|
||||
expect(vdtemp.seq, equals(0));
|
||||
expect(vdtemp.writer, equals(owner));
|
||||
|
||||
expect(await rc.getDHTValue(key, 0), isNull);
|
||||
|
||||
expect(await rc.setDHTValue(key, 0, vb), isNull);
|
||||
|
||||
expect(
|
||||
await rc.getDHTValue(key, 0, forceRefresh: true),
|
||||
equals(ValueData(
|
||||
data: vb,
|
||||
seq: 0,
|
||||
writer: owner,
|
||||
)));
|
||||
|
||||
expect(
|
||||
await rc.getDHTValue(key, 1, forceRefresh: true),
|
||||
equals(ValueData(
|
||||
data: va,
|
||||
seq: 0,
|
||||
writer: owner,
|
||||
)));
|
||||
|
||||
// Equal value should not trigger sequence number update
|
||||
expect(await rc.setDHTValue(key, 1, va), isNull);
|
||||
|
||||
// Different value should trigger sequence number update
|
||||
expect(await rc.setDHTValue(key, 1, vb), isNull);
|
||||
|
||||
// Now that we initialized some subkeys
|
||||
// and verified they stored correctly
|
||||
// Delete things locally and reopen and see if we can write
|
||||
// with the same writer key
|
||||
//
|
||||
|
||||
await rc.closeDHTRecord(key);
|
||||
await rc.deleteDHTRecord(key);
|
||||
|
||||
rec = await rc.openDHTRecord(key,
|
||||
writer: KeyPair(key: owner, secret: secret));
|
||||
expect(rec, isNotNull);
|
||||
expect(rec.key, equals(key));
|
||||
expect(rec.owner, equals(owner));
|
||||
expect(rec.ownerSecret, equals(secret));
|
||||
expect(rec.schema, isA<DHTSchemaDFLT>());
|
||||
expect(rec.schema.oCnt, equals(2));
|
||||
|
||||
// Verify subkey 1 can be set before it is get but newer is available online
|
||||
vdtemp = await rc.setDHTValue(key, 1, vc);
|
||||
expect(vdtemp, isNotNull);
|
||||
expect(vdtemp!.data, equals(vb));
|
||||
expect(vdtemp.seq, equals(1));
|
||||
expect(vdtemp.writer, equals(owner));
|
||||
|
||||
// Verify subkey 1 can be set a second time and it updates because seq is newer
|
||||
expect(await rc.setDHTValue(key, 1, vc), isNull);
|
||||
|
||||
// Verify the network got the subkey update with a refresh check
|
||||
vdtemp = await rc.getDHTValue(key, 1, forceRefresh: true);
|
||||
expect(vdtemp, isNotNull);
|
||||
expect(vdtemp!.data, equals(vc));
|
||||
expect(vdtemp.seq, equals(2));
|
||||
expect(vdtemp.writer, equals(owner));
|
||||
|
||||
// Delete things locally and reopen and see if we can write
|
||||
// with a different writer key (should fail)
|
||||
await rc.closeDHTRecord(key);
|
||||
await rc.deleteDHTRecord(key);
|
||||
|
||||
rec = await rc.openDHTRecord(key, writer: otherKeyPair);
|
||||
expect(rec, isNotNull);
|
||||
expect(rec.key, equals(key));
|
||||
expect(rec.owner, equals(owner));
|
||||
expect(rec.ownerSecret, isNull);
|
||||
expect(rec.schema, isA<DHTSchemaDFLT>());
|
||||
expect(rec.schema.oCnt, equals(2));
|
||||
|
||||
// Verify subkey 1 can NOT be set because we have the wrong writer
|
||||
await expectLater(() async => await rc.setDHTValue(key, 1, va),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
|
||||
// Verify subkey 0 can NOT be set because we have the wrong writer
|
||||
await expectLater(() async => await rc.setDHTValue(key, 0, va),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
|
||||
// Verify subkey 0 can be set because override with the right writer
|
||||
expect(
|
||||
await rc.setDHTValue(key, 0, va,
|
||||
writer: KeyPair(key: owner, secret: secret)),
|
||||
isNull);
|
||||
|
||||
// Clean up
|
||||
await rc.closeDHTRecord(key);
|
||||
await rc.deleteDHTRecord(key);
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testInspectDHTRecord() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
var rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 2));
|
||||
|
||||
expect(await rc.setDHTValue(rec.key, 0, utf8.encode("BLAH BLAH BLAH")),
|
||||
isNull);
|
||||
|
||||
final rr = await rc.inspectDHTRecord(rec.key);
|
||||
expect(rr.subkeys, equals([ValueSubkeyRange.make(0, 1)]));
|
||||
expect(rr.localSeqs, equals([0, 0xFFFFFFFF]));
|
||||
expect(rr.networkSeqs, equals([]));
|
||||
|
||||
final rr2 =
|
||||
await rc.inspectDHTRecord(rec.key, scope: DHTReportScope.syncGet);
|
||||
expect(rr2.subkeys, equals([ValueSubkeyRange.make(0, 1)]));
|
||||
expect(rr2.localSeqs, equals([0, 0xFFFFFFFF]));
|
||||
expect(rr2.networkSeqs, equals([0, 0xFFFFFFFF]));
|
||||
|
||||
await rc.closeDHTRecord(rec.key);
|
||||
await rc.deleteDHTRecord(rec.key);
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,263 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:veilid/veilid.dart';
|
||||
|
||||
Future<void> testRoutingContexts() async {
|
||||
{
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
rc.close();
|
||||
}
|
||||
|
||||
{
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
final rcp = rc.withDefaultSafety();
|
||||
rcp.close();
|
||||
rc.close();
|
||||
}
|
||||
|
||||
{
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
final rcp = rc.withSequencing(Sequencing.ensureOrdered);
|
||||
rcp.close();
|
||||
rc.close();
|
||||
}
|
||||
|
||||
{
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
final rcp = rc.withSafety(const SafetySelectionSafe(
|
||||
safetySpec: SafetySpec(
|
||||
hopCount: 2,
|
||||
stability: Stability.lowLatency,
|
||||
sequencing: Sequencing.noPreference)));
|
||||
rcp.close();
|
||||
rc.close();
|
||||
}
|
||||
{
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
final rcp = rc.withSafety(
|
||||
const SafetySelectionUnsafe(sequencing: Sequencing.preferOrdered));
|
||||
rcp.close();
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testAppMessageLoopback(Stream<VeilidUpdate> updateStream) async {
|
||||
final appMessageQueue = StreamController<VeilidAppMessage>();
|
||||
final appMessageSubscription = updateStream.listen((update) {
|
||||
if (update is VeilidAppMessage) {
|
||||
appMessageQueue.sink.add(update);
|
||||
}
|
||||
});
|
||||
try {
|
||||
await Veilid.instance.debug("purge routes");
|
||||
|
||||
// make a routing context that uses a safety route
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
// make a new local private route
|
||||
final prl = await Veilid.instance.newPrivateRoute();
|
||||
try {
|
||||
// import it as a remote route as well so we can send to it
|
||||
final prr = await Veilid.instance.importRemotePrivateRoute(prl.blob);
|
||||
try {
|
||||
// send an app message to our own private route
|
||||
final message = utf8.encode("abcd1234");
|
||||
await rc.appMessage(prr, message);
|
||||
|
||||
// we should get the same message back
|
||||
final update = await appMessageQueue.stream.first;
|
||||
expect(update.message, equals(message));
|
||||
expect(update.routeId, isNotNull);
|
||||
} finally {
|
||||
await Veilid.instance.releasePrivateRoute(prr);
|
||||
}
|
||||
} finally {
|
||||
await Veilid.instance.releasePrivateRoute(prl.routeId);
|
||||
}
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
} finally {
|
||||
await appMessageSubscription.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testAppCallLoopback(Stream<VeilidUpdate> updateStream) async {
|
||||
final appCallQueue = StreamController<VeilidAppCall>();
|
||||
final appMessageSubscription = updateStream.listen((update) {
|
||||
if (update is VeilidAppCall) {
|
||||
appCallQueue.sink.add(update);
|
||||
}
|
||||
});
|
||||
try {
|
||||
await Veilid.instance.debug("purge routes");
|
||||
|
||||
// make a routing context that uses a safety route
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
// make a new local private route
|
||||
final prl = await Veilid.instance.newPrivateRoute();
|
||||
try {
|
||||
// import it as a remote route as well so we can send to it
|
||||
final prr = await Veilid.instance.importRemotePrivateRoute(prl.blob);
|
||||
try {
|
||||
// send an app call to our own private route
|
||||
final message = utf8.encode("abcd1234");
|
||||
final appCallFuture = rc.appCall(prr, message);
|
||||
|
||||
// we should get the same call back
|
||||
final update = await appCallQueue.stream.first;
|
||||
final appcallid = update.callId;
|
||||
|
||||
expect(update.message, equals(message));
|
||||
expect(update.routeId, isNotNull);
|
||||
|
||||
// now we reply to the request
|
||||
final reply = utf8.encode("qwer5678");
|
||||
await Veilid.instance.appCallReply(appcallid, reply);
|
||||
|
||||
// now we should get the reply from the call
|
||||
final result = await appCallFuture;
|
||||
expect(result, equals(reply));
|
||||
} finally {
|
||||
await Veilid.instance.releasePrivateRoute(prr);
|
||||
}
|
||||
} finally {
|
||||
await Veilid.instance.releasePrivateRoute(prl.routeId);
|
||||
}
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
} finally {
|
||||
await appMessageSubscription.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testAppMessageLoopbackBigPackets(
|
||||
Stream<VeilidUpdate> updateStream) async {
|
||||
final appMessageQueue = StreamController<VeilidAppMessage>();
|
||||
final appMessageSubscription = updateStream.listen((update) {
|
||||
if (update is VeilidAppMessage) {
|
||||
appMessageQueue.sink.add(update);
|
||||
}
|
||||
});
|
||||
|
||||
final sentMessages = <String>{};
|
||||
final random = Random.secure();
|
||||
final cs = await Veilid.instance.bestCryptoSystem();
|
||||
|
||||
try {
|
||||
await Veilid.instance.debug("purge routes");
|
||||
|
||||
// make a routing context that uses a safety route
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
// make a new local private route
|
||||
final prl = await Veilid.instance.newPrivateRoute();
|
||||
try {
|
||||
// import it as a remote route as well so we can send to it
|
||||
final prr = await Veilid.instance.importRemotePrivateRoute(prl.blob);
|
||||
try {
|
||||
for (var i = 0; i < 5; i++) {
|
||||
// send an app message to our own private route
|
||||
final message = await cs.randomBytes(random.nextInt(32768));
|
||||
await rc.appMessage(prr, message);
|
||||
sentMessages.add(base64Url.encode(message));
|
||||
}
|
||||
|
||||
final appMessageQueueIterator =
|
||||
StreamIterator(appMessageQueue.stream);
|
||||
|
||||
// we should get the same messages back
|
||||
for (var i = 0; i < sentMessages.length; i++) {
|
||||
if (await appMessageQueueIterator.moveNext()) {
|
||||
final update = appMessageQueueIterator.current;
|
||||
expect(sentMessages.contains(base64Url.encode(update.message)),
|
||||
isTrue);
|
||||
} else {
|
||||
fail("not enough messages in the queue");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
await Veilid.instance.releasePrivateRoute(prr);
|
||||
}
|
||||
} finally {
|
||||
await Veilid.instance.releasePrivateRoute(prl.routeId);
|
||||
}
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
} finally {
|
||||
await appMessageSubscription.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testAppCallLoopbackBigPackets(
|
||||
Stream<VeilidUpdate> updateStream) async {
|
||||
final appCallQueue = StreamController<VeilidAppCall>();
|
||||
final appMessageSubscription = updateStream.listen((update) {
|
||||
if (update is VeilidAppCall) {
|
||||
appCallQueue.sink.add(update);
|
||||
}
|
||||
});
|
||||
final appCallQueueHandler = () async {
|
||||
await for (final update in appCallQueue.stream) {
|
||||
await Veilid.instance.appCallReply(update.callId, update.message);
|
||||
}
|
||||
}();
|
||||
|
||||
final sentMessages = <String>{};
|
||||
final random = Random.secure();
|
||||
final cs = await Veilid.instance.bestCryptoSystem();
|
||||
|
||||
try {
|
||||
await Veilid.instance.debug("purge routes");
|
||||
|
||||
// make a routing context that uses a safety route
|
||||
final rc = (await Veilid.instance.routingContext())
|
||||
.withSequencing(Sequencing.ensureOrdered, closeSelf: true);
|
||||
try {
|
||||
// make a new local private route
|
||||
final prl = await Veilid.instance
|
||||
.newCustomPrivateRoute(Stability.reliable, Sequencing.ensureOrdered);
|
||||
try {
|
||||
// import it as a remote route as well so we can send to it
|
||||
final prr = await Veilid.instance.importRemotePrivateRoute(prl.blob);
|
||||
try {
|
||||
for (var i = 0; i < 5; i++) {
|
||||
// send an app message to our own private route
|
||||
final message = await cs.randomBytes(random.nextInt(32768));
|
||||
final outmessage = await rc.appCall(prr, message);
|
||||
expect(message, equals(outmessage));
|
||||
}
|
||||
|
||||
final appMessageQueueIterator = StreamIterator(appCallQueue.stream);
|
||||
|
||||
// we should get the same messages back
|
||||
for (var i = 0; i < sentMessages.length; i++) {
|
||||
if (await appMessageQueueIterator.moveNext()) {
|
||||
final update = appMessageQueueIterator.current;
|
||||
expect(sentMessages.contains(base64Url.encode(update.message)),
|
||||
isTrue);
|
||||
} else {
|
||||
fail("not enough messages in the queue");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
await Veilid.instance.releasePrivateRoute(prr);
|
||||
}
|
||||
} finally {
|
||||
await Veilid.instance.releasePrivateRoute(prl.routeId);
|
||||
}
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
} finally {
|
||||
await appMessageSubscription.cancel();
|
||||
}
|
||||
await appCallQueue.close();
|
||||
await appCallQueueHandler;
|
||||
}
|
133
veilid-flutter/example/integration_test/test_table_db.dart
Normal file
133
veilid-flutter/example/integration_test/test_table_db.dart
Normal file
@ -0,0 +1,133 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:veilid/veilid.dart';
|
||||
|
||||
const testDb = "__dart_test_db";
|
||||
const testNonexistentDb = "__dart_test_nonexistent_db";
|
||||
|
||||
Future<void> testDeleteTableDbNonExistent() async {
|
||||
expect(await Veilid.instance.deleteTableDB(testNonexistentDb), isFalse);
|
||||
}
|
||||
|
||||
Future<void> testOpenDeleteTableDb() async {
|
||||
// delete test db if it exists
|
||||
await Veilid.instance.deleteTableDB(testDb);
|
||||
|
||||
final tdb = await Veilid.instance.openTableDB(testDb, 1);
|
||||
try {
|
||||
expect(() async => await Veilid.instance.deleteTableDB(testDb),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
} finally {
|
||||
tdb.close();
|
||||
}
|
||||
expect(await Veilid.instance.deleteTableDB(testDb), isTrue);
|
||||
}
|
||||
|
||||
Future<void> testOpenTwiceTableDb() async {
|
||||
// delete test db if it exists
|
||||
await Veilid.instance.deleteTableDB(testDb);
|
||||
|
||||
final tdb = await Veilid.instance.openTableDB(testDb, 1);
|
||||
final tdb2 = await Veilid.instance.openTableDB(testDb, 1);
|
||||
|
||||
// delete should fail because open
|
||||
await expectLater(() async => await Veilid.instance.deleteTableDB(testDb),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
tdb.close();
|
||||
// delete should fail because open
|
||||
await expectLater(() async => await Veilid.instance.deleteTableDB(testDb),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
tdb2.close();
|
||||
|
||||
// delete should now succeed
|
||||
expect(await Veilid.instance.deleteTableDB(testDb), isTrue);
|
||||
}
|
||||
|
||||
Future<void> testOpenTwiceTableDbStoreLoad() async {
|
||||
// delete test db if it exists
|
||||
await Veilid.instance.deleteTableDB(testDb);
|
||||
|
||||
final tdb = await Veilid.instance.openTableDB(testDb, 1);
|
||||
try {
|
||||
final tdb2 = await Veilid.instance.openTableDB(testDb, 1);
|
||||
try {
|
||||
// store into first db copy
|
||||
await tdb.store(0, utf8.encode("asdf"), utf8.encode("1234"));
|
||||
// load from second db copy
|
||||
expect(
|
||||
await tdb2.load(0, utf8.encode("asdf")), equals(utf8.encode("1234")));
|
||||
} finally {
|
||||
tdb2.close();
|
||||
}
|
||||
} finally {
|
||||
tdb.close();
|
||||
}
|
||||
|
||||
// delete should now succeed
|
||||
expect(await Veilid.instance.deleteTableDB(testDb), isTrue);
|
||||
}
|
||||
|
||||
Future<void> testOpenTwiceTableDbStoreDeleteLoad() async {
|
||||
// delete test db if it exists
|
||||
await Veilid.instance.deleteTableDB(testDb);
|
||||
|
||||
final tdb = await Veilid.instance.openTableDB(testDb, 1);
|
||||
try {
|
||||
final tdb2 = await Veilid.instance.openTableDB(testDb, 1);
|
||||
try {
|
||||
// store into first db copy
|
||||
await tdb.store(0, utf8.encode("asdf"), utf8.encode("1234"));
|
||||
// delete from second db copy and clean up
|
||||
await tdb2.delete(0, utf8.encode("asdf"));
|
||||
} finally {
|
||||
tdb2.close();
|
||||
}
|
||||
// load from first db copy
|
||||
expect(await tdb.load(0, utf8.encode("asdf")), isNull);
|
||||
} finally {
|
||||
tdb.close();
|
||||
}
|
||||
|
||||
// delete should now succeed
|
||||
expect(await Veilid.instance.deleteTableDB(testDb), isTrue);
|
||||
}
|
||||
|
||||
Future<void> testResizeTableDb() async {
|
||||
// delete test db if it exists
|
||||
await Veilid.instance.deleteTableDB(testDb);
|
||||
|
||||
final tdb = await Veilid.instance.openTableDB(testDb, 1);
|
||||
try {
|
||||
// reopen the db with more columns should fail if it is already open
|
||||
await expectLater(() async => await Veilid.instance.openTableDB(testDb, 2),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
} finally {
|
||||
tdb.close();
|
||||
}
|
||||
|
||||
final tdb2 = await Veilid.instance.openTableDB(testDb, 2);
|
||||
try {
|
||||
// write something to second column
|
||||
await tdb2.store(1, utf8.encode("qwer"), utf8.encode("5678"));
|
||||
|
||||
// reopen the db with fewer columns
|
||||
final tdb3 = await Veilid.instance.openTableDB(testDb, 1);
|
||||
try {
|
||||
// Should fail access to second column
|
||||
await expectLater(() async => await tdb3.load(1, utf8.encode("qwer")),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
|
||||
// Should succeed with access to second column
|
||||
expect(
|
||||
await tdb2.load(1, utf8.encode("qwer")), equals(utf8.encode("5678")));
|
||||
} finally {
|
||||
tdb3.close();
|
||||
}
|
||||
} finally {
|
||||
tdb2.close();
|
||||
}
|
||||
|
||||
// delete should now succeed
|
||||
expect(await Veilid.instance.deleteTableDB(testDb), isTrue);
|
||||
}
|
@ -240,6 +240,31 @@ class RouteBlob with _$RouteBlob {
|
||||
_$RouteBlobFromJson(json as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
/// Inspect
|
||||
@freezed
|
||||
class DHTRecordReport with _$DHTRecordReport {
|
||||
const factory DHTRecordReport({
|
||||
required List<ValueSubkeyRange> subkeys,
|
||||
required List<int> localSeqs,
|
||||
required List<int> networkSeqs,
|
||||
}) = _DHTRecordReport;
|
||||
factory DHTRecordReport.fromJson(dynamic json) =>
|
||||
_$DHTRecordReportFromJson(json as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
enum DHTReportScope {
|
||||
local,
|
||||
syncGet,
|
||||
syncSet,
|
||||
updateGet,
|
||||
updateSet;
|
||||
|
||||
factory DHTReportScope.fromJson(dynamic j) =>
|
||||
DHTReportScope.values.byName((j as String).toCamelCase());
|
||||
String toJson() => name.toPascalCase();
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
/// VeilidRoutingContext
|
||||
|
||||
@ -247,9 +272,11 @@ abstract class VeilidRoutingContext {
|
||||
void close();
|
||||
|
||||
// Modifiers
|
||||
VeilidRoutingContext withDefaultSafety();
|
||||
VeilidRoutingContext withSafety(SafetySelection safetySelection);
|
||||
VeilidRoutingContext withSequencing(Sequencing sequencing);
|
||||
VeilidRoutingContext withDefaultSafety({bool closeSelf = false});
|
||||
VeilidRoutingContext withSafety(SafetySelection safetySelection,
|
||||
{bool closeSelf = false});
|
||||
VeilidRoutingContext withSequencing(Sequencing sequencing,
|
||||
{bool closeSelf = false});
|
||||
Future<SafetySelection> safety();
|
||||
|
||||
// App call/message
|
||||
@ -269,4 +296,7 @@ abstract class VeilidRoutingContext {
|
||||
Future<Timestamp> watchDHTValues(TypedKey key,
|
||||
{List<ValueSubkeyRange>? subkeys, Timestamp? expiration, int? count});
|
||||
Future<bool> cancelDHTWatch(TypedKey key, {List<ValueSubkeyRange>? subkeys});
|
||||
Future<DHTRecordReport> inspectDHTRecord(TypedKey key,
|
||||
{List<ValueSubkeyRange>? subkeys,
|
||||
DHTReportScope scope = DHTReportScope.local});
|
||||
}
|
||||
|
@ -1355,3 +1355,210 @@ abstract class _RouteBlob implements RouteBlob {
|
||||
_$$RouteBlobImplCopyWith<_$RouteBlobImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
DHTRecordReport _$DHTRecordReportFromJson(Map<String, dynamic> json) {
|
||||
return _DHTRecordReport.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$DHTRecordReport {
|
||||
List<ValueSubkeyRange> get subkeys => throw _privateConstructorUsedError;
|
||||
List<int> get localSeqs => throw _privateConstructorUsedError;
|
||||
List<int> get networkSeqs => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$DHTRecordReportCopyWith<DHTRecordReport> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $DHTRecordReportCopyWith<$Res> {
|
||||
factory $DHTRecordReportCopyWith(
|
||||
DHTRecordReport value, $Res Function(DHTRecordReport) then) =
|
||||
_$DHTRecordReportCopyWithImpl<$Res, DHTRecordReport>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{List<ValueSubkeyRange> subkeys,
|
||||
List<int> localSeqs,
|
||||
List<int> networkSeqs});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$DHTRecordReportCopyWithImpl<$Res, $Val extends DHTRecordReport>
|
||||
implements $DHTRecordReportCopyWith<$Res> {
|
||||
_$DHTRecordReportCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? subkeys = null,
|
||||
Object? localSeqs = null,
|
||||
Object? networkSeqs = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
subkeys: null == subkeys
|
||||
? _value.subkeys
|
||||
: subkeys // ignore: cast_nullable_to_non_nullable
|
||||
as List<ValueSubkeyRange>,
|
||||
localSeqs: null == localSeqs
|
||||
? _value.localSeqs
|
||||
: localSeqs // ignore: cast_nullable_to_non_nullable
|
||||
as List<int>,
|
||||
networkSeqs: null == networkSeqs
|
||||
? _value.networkSeqs
|
||||
: networkSeqs // ignore: cast_nullable_to_non_nullable
|
||||
as List<int>,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$DHTRecordReportImplCopyWith<$Res>
|
||||
implements $DHTRecordReportCopyWith<$Res> {
|
||||
factory _$$DHTRecordReportImplCopyWith(_$DHTRecordReportImpl value,
|
||||
$Res Function(_$DHTRecordReportImpl) then) =
|
||||
__$$DHTRecordReportImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{List<ValueSubkeyRange> subkeys,
|
||||
List<int> localSeqs,
|
||||
List<int> networkSeqs});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$DHTRecordReportImplCopyWithImpl<$Res>
|
||||
extends _$DHTRecordReportCopyWithImpl<$Res, _$DHTRecordReportImpl>
|
||||
implements _$$DHTRecordReportImplCopyWith<$Res> {
|
||||
__$$DHTRecordReportImplCopyWithImpl(
|
||||
_$DHTRecordReportImpl _value, $Res Function(_$DHTRecordReportImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? subkeys = null,
|
||||
Object? localSeqs = null,
|
||||
Object? networkSeqs = null,
|
||||
}) {
|
||||
return _then(_$DHTRecordReportImpl(
|
||||
subkeys: null == subkeys
|
||||
? _value._subkeys
|
||||
: subkeys // ignore: cast_nullable_to_non_nullable
|
||||
as List<ValueSubkeyRange>,
|
||||
localSeqs: null == localSeqs
|
||||
? _value._localSeqs
|
||||
: localSeqs // ignore: cast_nullable_to_non_nullable
|
||||
as List<int>,
|
||||
networkSeqs: null == networkSeqs
|
||||
? _value._networkSeqs
|
||||
: networkSeqs // ignore: cast_nullable_to_non_nullable
|
||||
as List<int>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$DHTRecordReportImpl implements _DHTRecordReport {
|
||||
const _$DHTRecordReportImpl(
|
||||
{required final List<ValueSubkeyRange> subkeys,
|
||||
required final List<int> localSeqs,
|
||||
required final List<int> networkSeqs})
|
||||
: _subkeys = subkeys,
|
||||
_localSeqs = localSeqs,
|
||||
_networkSeqs = networkSeqs;
|
||||
|
||||
factory _$DHTRecordReportImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$DHTRecordReportImplFromJson(json);
|
||||
|
||||
final List<ValueSubkeyRange> _subkeys;
|
||||
@override
|
||||
List<ValueSubkeyRange> get subkeys {
|
||||
if (_subkeys is EqualUnmodifiableListView) return _subkeys;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_subkeys);
|
||||
}
|
||||
|
||||
final List<int> _localSeqs;
|
||||
@override
|
||||
List<int> get localSeqs {
|
||||
if (_localSeqs is EqualUnmodifiableListView) return _localSeqs;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_localSeqs);
|
||||
}
|
||||
|
||||
final List<int> _networkSeqs;
|
||||
@override
|
||||
List<int> get networkSeqs {
|
||||
if (_networkSeqs is EqualUnmodifiableListView) return _networkSeqs;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_networkSeqs);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DHTRecordReport(subkeys: $subkeys, localSeqs: $localSeqs, networkSeqs: $networkSeqs)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$DHTRecordReportImpl &&
|
||||
const DeepCollectionEquality().equals(other._subkeys, _subkeys) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._localSeqs, _localSeqs) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._networkSeqs, _networkSeqs));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(_subkeys),
|
||||
const DeepCollectionEquality().hash(_localSeqs),
|
||||
const DeepCollectionEquality().hash(_networkSeqs));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$DHTRecordReportImplCopyWith<_$DHTRecordReportImpl> get copyWith =>
|
||||
__$$DHTRecordReportImplCopyWithImpl<_$DHTRecordReportImpl>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$DHTRecordReportImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _DHTRecordReport implements DHTRecordReport {
|
||||
const factory _DHTRecordReport(
|
||||
{required final List<ValueSubkeyRange> subkeys,
|
||||
required final List<int> localSeqs,
|
||||
required final List<int> networkSeqs}) = _$DHTRecordReportImpl;
|
||||
|
||||
factory _DHTRecordReport.fromJson(Map<String, dynamic> json) =
|
||||
_$DHTRecordReportImpl.fromJson;
|
||||
|
||||
@override
|
||||
List<ValueSubkeyRange> get subkeys;
|
||||
@override
|
||||
List<int> get localSeqs;
|
||||
@override
|
||||
List<int> get networkSeqs;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$DHTRecordReportImplCopyWith<_$DHTRecordReportImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
@ -109,3 +109,23 @@ Map<String, dynamic> _$$RouteBlobImplToJson(_$RouteBlobImpl instance) =>
|
||||
'route_id': instance.routeId,
|
||||
'blob': const Uint8ListJsonConverter().toJson(instance.blob),
|
||||
};
|
||||
|
||||
_$DHTRecordReportImpl _$$DHTRecordReportImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$DHTRecordReportImpl(
|
||||
subkeys: (json['subkeys'] as List<dynamic>)
|
||||
.map(ValueSubkeyRange.fromJson)
|
||||
.toList(),
|
||||
localSeqs:
|
||||
(json['local_seqs'] as List<dynamic>).map((e) => e as int).toList(),
|
||||
networkSeqs:
|
||||
(json['network_seqs'] as List<dynamic>).map((e) => e as int).toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$DHTRecordReportImplToJson(
|
||||
_$DHTRecordReportImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'subkeys': instance.subkeys.map((e) => e.toJson()).toList(),
|
||||
'local_seqs': instance.localSeqs,
|
||||
'network_seqs': instance.networkSeqs,
|
||||
};
|
||||
|
@ -94,6 +94,10 @@ typedef _RoutingContextWatchDHTValuesDart = void Function(
|
||||
// id: u32, key: FfiStr, subkeys: FfiStr)
|
||||
typedef _RoutingContextCancelDHTWatchDart = void Function(
|
||||
int, int, Pointer<Utf8>, Pointer<Utf8>);
|
||||
// fn routing_context_inspect_dht_record(port: i64,
|
||||
// id: u32, key: FfiStr, subkeys: FfiStr, scope: FfiStr)
|
||||
typedef _RoutingContextInspectDHTRecordDart = void Function(
|
||||
int, int, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>);
|
||||
|
||||
// fn new_private_route(port: i64)
|
||||
typedef _NewPrivateRouteDart = void Function(int);
|
||||
@ -534,26 +538,40 @@ class VeilidRoutingContextFFI extends VeilidRoutingContext {
|
||||
}
|
||||
|
||||
@override
|
||||
VeilidRoutingContextFFI withDefaultSafety() {
|
||||
VeilidRoutingContextFFI withDefaultSafety({bool closeSelf = false}) {
|
||||
_ctx.ensureValid();
|
||||
final newId = _ctx.ffi._routingContextWithDefaultSafety(_ctx.id!);
|
||||
return VeilidRoutingContextFFI._(_Ctx(newId, _ctx.ffi));
|
||||
final out = VeilidRoutingContextFFI._(_Ctx(newId, _ctx.ffi));
|
||||
if (closeSelf) {
|
||||
close();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@override
|
||||
VeilidRoutingContextFFI withSafety(SafetySelection safetySelection) {
|
||||
VeilidRoutingContextFFI withSafety(SafetySelection safetySelection,
|
||||
{bool closeSelf = false}) {
|
||||
_ctx.ensureValid();
|
||||
final newId = _ctx.ffi._routingContextWithSafety(
|
||||
_ctx.id!, jsonEncode(safetySelection).toNativeUtf8());
|
||||
return VeilidRoutingContextFFI._(_Ctx(newId, _ctx.ffi));
|
||||
final out = VeilidRoutingContextFFI._(_Ctx(newId, _ctx.ffi));
|
||||
if (closeSelf) {
|
||||
close();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@override
|
||||
VeilidRoutingContextFFI withSequencing(Sequencing sequencing) {
|
||||
VeilidRoutingContextFFI withSequencing(Sequencing sequencing,
|
||||
{bool closeSelf = false}) {
|
||||
_ctx.ensureValid();
|
||||
final newId = _ctx.ffi._routingContextWithSequencing(
|
||||
_ctx.id!, jsonEncode(sequencing).toNativeUtf8());
|
||||
return VeilidRoutingContextFFI._(_Ctx(newId, _ctx.ffi));
|
||||
final out = VeilidRoutingContextFFI._(_Ctx(newId, _ctx.ffi));
|
||||
if (closeSelf) {
|
||||
close();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -717,6 +735,26 @@ class VeilidRoutingContextFFI extends VeilidRoutingContext {
|
||||
final cancelled = await processFuturePlain<bool>(recvPort.first);
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DHTRecordReport> inspectDHTRecord(TypedKey key,
|
||||
{List<ValueSubkeyRange>? subkeys,
|
||||
DHTReportScope scope = DHTReportScope.local}) async {
|
||||
subkeys ??= [];
|
||||
|
||||
_ctx.ensureValid();
|
||||
final nativeKey = jsonEncode(key).toNativeUtf8();
|
||||
final nativeSubkeys = jsonEncode(subkeys).toNativeUtf8();
|
||||
final nativeScope = jsonEncode(scope).toNativeUtf8();
|
||||
|
||||
final recvPort = ReceivePort('routing_context_inspect_dht_record');
|
||||
final sendPort = recvPort.sendPort;
|
||||
_ctx.ffi._routingContextInspectDHTRecord(
|
||||
sendPort.nativePort, _ctx.id!, nativeKey, nativeSubkeys, nativeScope);
|
||||
final report =
|
||||
await processFutureJson(DHTRecordReport.fromJson, recvPort.first);
|
||||
return report;
|
||||
}
|
||||
}
|
||||
|
||||
class _TDBT {
|
||||
@ -1259,6 +1297,11 @@ class VeilidFFI extends Veilid {
|
||||
Void Function(Int64, Uint32, Pointer<Utf8>, Pointer<Utf8>),
|
||||
_RoutingContextCancelDHTWatchDart>(
|
||||
'routing_context_cancel_dht_watch'),
|
||||
_routingContextInspectDHTRecord = dylib.lookupFunction<
|
||||
Void Function(
|
||||
Int64, Uint32, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>),
|
||||
_RoutingContextInspectDHTRecordDart>(
|
||||
'routing_context_inspect_dht_record'),
|
||||
_newPrivateRoute =
|
||||
dylib.lookupFunction<Void Function(Int64), _NewPrivateRouteDart>(
|
||||
'new_private_route'),
|
||||
@ -1402,9 +1445,14 @@ class VeilidFFI extends Veilid {
|
||||
_DefaultVeilidConfigDart>('default_veilid_config') {
|
||||
// Get veilid_flutter initializer
|
||||
final initializeVeilidFlutter = _dylib.lookupFunction<
|
||||
Void Function(Pointer<_DartPostCObject>),
|
||||
void Function(Pointer<_DartPostCObject>)>('initialize_veilid_flutter');
|
||||
initializeVeilidFlutter(NativeApi.postCObject);
|
||||
Void Function(Pointer<_DartPostCObject>, Pointer<Utf8>),
|
||||
void Function(Pointer<_DartPostCObject>,
|
||||
Pointer<Utf8>)>('initialize_veilid_flutter');
|
||||
initializeVeilidFlutter(
|
||||
NativeApi.postCObject,
|
||||
// ignore: avoid_redundant_argument_values, do_not_use_environment
|
||||
const String.fromEnvironment('VEILID_CRASH_PATH').toNativeUtf8(),
|
||||
);
|
||||
}
|
||||
// veilid_core shared library
|
||||
final DynamicLibrary _dylib;
|
||||
@ -1436,6 +1484,7 @@ class VeilidFFI extends Veilid {
|
||||
final _RoutingContextSetDHTValueDart _routingContextSetDHTValue;
|
||||
final _RoutingContextWatchDHTValuesDart _routingContextWatchDHTValues;
|
||||
final _RoutingContextCancelDHTWatchDart _routingContextCancelDHTWatch;
|
||||
final _RoutingContextInspectDHTRecordDart _routingContextInspectDHTRecord;
|
||||
|
||||
final _NewPrivateRouteDart _newPrivateRoute;
|
||||
final _NewCustomPrivateRouteDart _newCustomPrivateRoute;
|
||||
|
@ -68,28 +68,41 @@ class VeilidRoutingContextJS extends VeilidRoutingContext {
|
||||
}
|
||||
|
||||
@override
|
||||
VeilidRoutingContextJS withDefaultSafety() {
|
||||
VeilidRoutingContextJS withDefaultSafety({bool closeSelf = false}) {
|
||||
final id = _ctx.requireId();
|
||||
final int newId =
|
||||
js_util.callMethod(wasm, 'routing_context_with_default_safety', [id]);
|
||||
return VeilidRoutingContextJS._(_Ctx(newId, _ctx.js));
|
||||
final out = VeilidRoutingContextJS._(_Ctx(newId, _ctx.js));
|
||||
if (closeSelf) {
|
||||
close();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@override
|
||||
VeilidRoutingContextJS withSafety(SafetySelection safetySelection) {
|
||||
VeilidRoutingContextJS withSafety(SafetySelection safetySelection,
|
||||
{bool closeSelf = false}) {
|
||||
final id = _ctx.requireId();
|
||||
final newId = js_util.callMethod<int>(
|
||||
wasm, 'routing_context_with_safety', [id, jsonEncode(safetySelection)]);
|
||||
|
||||
return VeilidRoutingContextJS._(_Ctx(newId, _ctx.js));
|
||||
final out = VeilidRoutingContextJS._(_Ctx(newId, _ctx.js));
|
||||
if (closeSelf) {
|
||||
close();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@override
|
||||
VeilidRoutingContextJS withSequencing(Sequencing sequencing) {
|
||||
VeilidRoutingContextJS withSequencing(Sequencing sequencing,
|
||||
{bool closeSelf = false}) {
|
||||
final id = _ctx.requireId();
|
||||
final newId = js_util.callMethod<int>(
|
||||
wasm, 'routing_context_with_sequencing', [id, jsonEncode(sequencing)]);
|
||||
return VeilidRoutingContextJS._(_Ctx(newId, _ctx.js));
|
||||
final out = VeilidRoutingContextJS._(_Ctx(newId, _ctx.js));
|
||||
if (closeSelf) {
|
||||
close();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -219,6 +232,18 @@ class VeilidRoutingContextJS extends VeilidRoutingContext {
|
||||
'routing_context_cancel_dht_watch',
|
||||
[id, jsonEncode(key), jsonEncode(subkeys)]));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DHTRecordReport> inspectDHTRecord(TypedKey key,
|
||||
{List<ValueSubkeyRange>? subkeys,
|
||||
DHTReportScope scope = DHTReportScope.local}) async {
|
||||
subkeys ??= [];
|
||||
|
||||
final id = _ctx.requireId();
|
||||
return DHTRecordReport.fromJson(jsonDecode(await _wrapApiPromise(js_util
|
||||
.callMethod(wasm, 'routing_context_inspect_dht_record',
|
||||
[id, jsonEncode(key), jsonEncode(subkeys), jsonEncode(scope)]))));
|
||||
}
|
||||
}
|
||||
|
||||
// JS implementation of VeilidCryptoSystem
|
||||
|
4
veilid-flutter/run_integration_tests.sh
Executable file
4
veilid-flutter/run_integration_tests.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
pushd example 2>/dev/null
|
||||
flutter test -r expanded integration_test/app_test.dart $@
|
||||
popd 2>/dev/null
|
2
veilid-flutter/run_unit_tests.sh
Executable file
2
veilid-flutter/run_unit_tests.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
flutter test -r expanded $@
|
@ -9,12 +9,12 @@ use opentelemetry::*;
|
||||
use opentelemetry_otlp::WithExportConfig;
|
||||
use parking_lot::Mutex;
|
||||
use serde::*;
|
||||
use std::io::Write;
|
||||
use std::os::raw::c_char;
|
||||
use std::sync::Arc;
|
||||
use tracing::*;
|
||||
use tracing_subscriber::prelude::*;
|
||||
use veilid_core::tools::*;
|
||||
use veilid_core::Encodable as _;
|
||||
use veilid_core::{tools::*, Encodable};
|
||||
|
||||
// Detect flutter load/unload
|
||||
cfg_if! {
|
||||
@ -141,36 +141,56 @@ pub struct VeilidFFIRouteBlob {
|
||||
// Initializer
|
||||
#[no_mangle]
|
||||
#[instrument]
|
||||
pub extern "C" fn initialize_veilid_flutter(dart_post_c_object_ptr: ffi::DartPostCObjectFnType) {
|
||||
pub extern "C" fn initialize_veilid_flutter(
|
||||
dart_post_c_object_ptr: ffi::DartPostCObjectFnType,
|
||||
crash_path: FfiStr,
|
||||
) {
|
||||
unsafe {
|
||||
store_dart_post_cobject(dart_post_c_object_ptr);
|
||||
}
|
||||
let crash_path = crash_path.into_opt_string().unwrap_or_default();
|
||||
|
||||
use std::sync::Once;
|
||||
static INIT_BACKTRACE: Once = Once::new();
|
||||
INIT_BACKTRACE.call_once(move || {
|
||||
std::env::set_var("RUST_BACKTRACE", "1");
|
||||
std::panic::set_hook(Box::new(move |panic_info| {
|
||||
let crash_file = if crash_path.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(std::fs::File::create(&crash_path).unwrap())
|
||||
};
|
||||
|
||||
let (file, line) = if let Some(loc) = panic_info.location() {
|
||||
(loc.file(), loc.line())
|
||||
} else {
|
||||
("<unknown>", 0)
|
||||
};
|
||||
eprintln!("### Rust `panic!` hit at file '{}', line {}", file, line);
|
||||
|
||||
let mut out = String::new();
|
||||
|
||||
out += &format!("### Rust `panic!` hit at file '{}', line {}\n", file, line);
|
||||
if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
|
||||
eprintln!("panic payload: {:?}", s);
|
||||
out += &format!("panic payload: {:?}\n", s);
|
||||
} else if let Some(s) = panic_info.payload().downcast_ref::<String>() {
|
||||
eprintln!("panic payload: {:?}", s);
|
||||
out += &format!("panic payload: {:?}\n", s);
|
||||
} else if let Some(a) = panic_info.payload().downcast_ref::<std::fmt::Arguments>() {
|
||||
eprintln!("panic payload: {:?}", a);
|
||||
out += &format!("panic payload: {:?}\n", a);
|
||||
} else {
|
||||
eprintln!("no panic payload");
|
||||
out += "no panic payload\n";
|
||||
}
|
||||
eprintln!(
|
||||
out += &format!(
|
||||
" Complete stack trace:\n{:?}\n",
|
||||
backtrace::Backtrace::new()
|
||||
);
|
||||
|
||||
if let Some(mut crash_file) = crash_file {
|
||||
write!(crash_file, "{}", out).unwrap();
|
||||
crash_file.flush().unwrap();
|
||||
} else {
|
||||
eprintln!("{}", out);
|
||||
}
|
||||
|
||||
// And stop the process, no recovery is going to be possible here
|
||||
eprintln!("aborting!");
|
||||
std::process::abort();
|
||||
@ -521,20 +541,20 @@ pub extern "C" fn routing_context_with_sequencing(id: u32, sequencing: FfiStr) -
|
||||
add_routing_context(&mut rc, routing_context)
|
||||
}
|
||||
|
||||
fn get_routing_context(id: u32, func_name: &str) -> APIResult<veilid_core::RoutingContext> {
|
||||
let rc = ROUTING_CONTEXTS.lock();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
func_name, "id", id,
|
||||
));
|
||||
};
|
||||
Ok(routing_context.clone())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn routing_context_safety(port: i64, id: u32) {
|
||||
DartIsolateWrapper::new(port).spawn_result_json(async move {
|
||||
let routing_context = {
|
||||
let rc = ROUTING_CONTEXTS.lock();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_app_call",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_safety")?;
|
||||
APIResult::Ok(routing_context.safety())
|
||||
});
|
||||
}
|
||||
@ -545,17 +565,8 @@ pub extern "C" fn routing_context_app_call(port: i64, id: u32, target: FfiStr, r
|
||||
.decode(request.into_opt_string().unwrap().as_bytes())
|
||||
.unwrap();
|
||||
DartIsolateWrapper::new(port).spawn_result(async move {
|
||||
let routing_context = {
|
||||
let rc = ROUTING_CONTEXTS.lock();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_app_call",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_app_call")?;
|
||||
|
||||
let veilid_api = get_veilid_api().await?;
|
||||
let target = veilid_api.parse_as_target(target_string).await?;
|
||||
let answer = routing_context.app_call(target, request).await?;
|
||||
@ -571,17 +582,7 @@ pub extern "C" fn routing_context_app_message(port: i64, id: u32, target: FfiStr
|
||||
.decode(message.into_opt_string().unwrap().as_bytes())
|
||||
.unwrap();
|
||||
DartIsolateWrapper::new(port).spawn_result(async move {
|
||||
let routing_context = {
|
||||
let rc = ROUTING_CONTEXTS.lock();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_app_message",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_app_message")?;
|
||||
|
||||
let veilid_api = get_veilid_api().await?;
|
||||
let target = veilid_api.parse_as_target(target_string).await?;
|
||||
@ -601,17 +602,7 @@ pub extern "C" fn routing_context_create_dht_record(port: i64, id: u32, schema:
|
||||
veilid_core::deserialize_opt_json(schema.into_opt_string()).unwrap();
|
||||
|
||||
DartIsolateWrapper::new(port).spawn_result_json(async move {
|
||||
let routing_context = {
|
||||
let rc = ROUTING_CONTEXTS.lock();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_create_dht_record",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_create_dht_record")?;
|
||||
|
||||
let dht_record_descriptor = routing_context
|
||||
.create_dht_record(schema, crypto_kind)
|
||||
@ -628,17 +619,8 @@ pub extern "C" fn routing_context_open_dht_record(port: i64, id: u32, key: FfiSt
|
||||
.into_opt_string()
|
||||
.map(|s| veilid_core::deserialize_json(&s).unwrap());
|
||||
DartIsolateWrapper::new(port).spawn_result_json(async move {
|
||||
let routing_context = {
|
||||
let rc = ROUTING_CONTEXTS.lock();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_open_dht_record",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_open_dht_record")?;
|
||||
|
||||
let dht_record_descriptor = routing_context.open_dht_record(key, writer).await?;
|
||||
APIResult::Ok(dht_record_descriptor)
|
||||
});
|
||||
@ -649,17 +631,8 @@ pub extern "C" fn routing_context_close_dht_record(port: i64, id: u32, key: FfiS
|
||||
let key: veilid_core::TypedKey =
|
||||
veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap();
|
||||
DartIsolateWrapper::new(port).spawn_result(async move {
|
||||
let routing_context = {
|
||||
let rc = ROUTING_CONTEXTS.lock();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_close_dht_record",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_close_dht_record")?;
|
||||
|
||||
routing_context.close_dht_record(key).await?;
|
||||
APIRESULT_VOID
|
||||
});
|
||||
@ -670,17 +643,8 @@ pub extern "C" fn routing_context_delete_dht_record(port: i64, id: u32, key: Ffi
|
||||
let key: veilid_core::TypedKey =
|
||||
veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap();
|
||||
DartIsolateWrapper::new(port).spawn_result(async move {
|
||||
let routing_context = {
|
||||
let rc = ROUTING_CONTEXTS.lock();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_delete_dht_record",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_delete_dht_record")?;
|
||||
|
||||
routing_context.delete_dht_record(key).await?;
|
||||
APIRESULT_VOID
|
||||
});
|
||||
@ -697,17 +661,8 @@ pub extern "C" fn routing_context_get_dht_value(
|
||||
let key: veilid_core::TypedKey =
|
||||
veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap();
|
||||
DartIsolateWrapper::new(port).spawn_result_json(async move {
|
||||
let routing_context = {
|
||||
let rc = ROUTING_CONTEXTS.lock();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_get_dht_value",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_get_dht_value")?;
|
||||
|
||||
let res = routing_context
|
||||
.get_dht_value(key, subkey, force_refresh)
|
||||
.await?;
|
||||
@ -734,17 +689,8 @@ pub extern "C" fn routing_context_set_dht_value(
|
||||
.map(|s| veilid_core::deserialize_json(&s).unwrap());
|
||||
|
||||
DartIsolateWrapper::new(port).spawn_result_json(async move {
|
||||
let routing_context = {
|
||||
let rc = ROUTING_CONTEXTS.lock();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_set_dht_value",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_set_dht_value")?;
|
||||
|
||||
let res = routing_context
|
||||
.set_dht_value(key, subkey, data, writer)
|
||||
.await?;
|
||||
@ -768,17 +714,8 @@ pub extern "C" fn routing_context_watch_dht_values(
|
||||
let expiration = veilid_core::Timestamp::from(expiration);
|
||||
|
||||
DartIsolateWrapper::new(port).spawn_result(async move {
|
||||
let routing_context = {
|
||||
let rc = ROUTING_CONTEXTS.lock();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_watch_dht_values",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_watch_dht_values")?;
|
||||
|
||||
let res = routing_context
|
||||
.watch_dht_values(key, subkeys, expiration, count)
|
||||
.await?;
|
||||
@ -799,22 +736,38 @@ pub extern "C" fn routing_context_cancel_dht_watch(
|
||||
veilid_core::deserialize_opt_json(subkeys.into_opt_string()).unwrap();
|
||||
|
||||
DartIsolateWrapper::new(port).spawn_result(async move {
|
||||
let routing_context = {
|
||||
let rc = ROUTING_CONTEXTS.lock();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_set_dht_value",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_cancel_dht_watch")?;
|
||||
|
||||
let res = routing_context.cancel_dht_watch(key, subkeys).await?;
|
||||
APIResult::Ok(res)
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn routing_context_inspect_dht_record(
|
||||
port: i64,
|
||||
id: u32,
|
||||
key: FfiStr,
|
||||
subkeys: FfiStr,
|
||||
scope: FfiStr,
|
||||
) {
|
||||
let key: veilid_core::TypedKey =
|
||||
veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap();
|
||||
let subkeys: veilid_core::ValueSubkeyRangeSet =
|
||||
veilid_core::deserialize_opt_json(subkeys.into_opt_string()).unwrap();
|
||||
let scope: veilid_core::DHTReportScope =
|
||||
veilid_core::deserialize_opt_json(scope.into_opt_string()).unwrap();
|
||||
|
||||
DartIsolateWrapper::new(port).spawn_result_json(async move {
|
||||
let routing_context = get_routing_context(id, "routing_context_inspect_dht_record")?;
|
||||
|
||||
let res = routing_context
|
||||
.inspect_dht_record(key, subkeys, scope)
|
||||
.await?;
|
||||
APIResult::Ok(res)
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn new_private_route(port: i64) {
|
||||
DartIsolateWrapper::new(port).spawn_result_json(async move {
|
||||
@ -851,25 +804,20 @@ pub extern "C" fn new_custom_private_route(port: i64, stability: FfiStr, sequenc
|
||||
#[no_mangle]
|
||||
pub extern "C" fn import_remote_private_route(port: i64, blob: FfiStr) {
|
||||
let blob: Vec<u8> = data_encoding::BASE64URL_NOPAD
|
||||
.decode(
|
||||
veilid_core::deserialize_opt_json::<String>(blob.into_opt_string())
|
||||
.unwrap()
|
||||
.as_bytes(),
|
||||
)
|
||||
.decode(blob.into_opt_string().unwrap().as_bytes())
|
||||
.unwrap();
|
||||
DartIsolateWrapper::new(port).spawn_result(async move {
|
||||
let veilid_api = get_veilid_api().await?;
|
||||
|
||||
let route_id = veilid_api.import_remote_private_route(blob)?;
|
||||
let route_id = veilid_api.import_remote_private_route(blob)?.encode();
|
||||
|
||||
APIResult::Ok(route_id.encode())
|
||||
APIResult::Ok(route_id)
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn release_private_route(port: i64, route_id: FfiStr) {
|
||||
let route_id: veilid_core::RouteId =
|
||||
veilid_core::deserialize_opt_json(route_id.into_opt_string()).unwrap();
|
||||
let route_id = veilid_core::RouteId::try_decode(route_id.into_string()).unwrap();
|
||||
DartIsolateWrapper::new(port).spawn_result(async move {
|
||||
let veilid_api = get_veilid_api().await?;
|
||||
veilid_api.release_private_route(route_id)?;
|
||||
@ -880,7 +828,10 @@ pub extern "C" fn release_private_route(port: i64, route_id: FfiStr) {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn app_call_reply(port: i64, call_id: FfiStr, message: FfiStr) {
|
||||
let call_id = call_id.into_opt_string().unwrap_or_default();
|
||||
let message = message.into_opt_string().unwrap_or_default();
|
||||
let message = data_encoding::BASE64URL_NOPAD
|
||||
.decode(message.into_opt_string().unwrap().as_bytes())
|
||||
.unwrap();
|
||||
|
||||
DartIsolateWrapper::new(port).spawn_result(async move {
|
||||
let call_id = match call_id.parse() {
|
||||
Ok(v) => v,
|
||||
@ -890,9 +841,7 @@ pub extern "C" fn app_call_reply(port: i64, call_id: FfiStr, message: FfiStr) {
|
||||
))
|
||||
}
|
||||
};
|
||||
let message = data_encoding::BASE64URL_NOPAD
|
||||
.decode(message.as_bytes())
|
||||
.map_err(|e| veilid_core::VeilidAPIError::invalid_argument(e, "message", message))?;
|
||||
|
||||
let veilid_api = get_veilid_api().await?;
|
||||
veilid_api.app_call_reply(call_id, message).await?;
|
||||
APIRESULT_VOID
|
||||
@ -959,20 +908,20 @@ pub extern "C" fn table_db_get_column_count(id: u32) -> u32 {
|
||||
cc
|
||||
}
|
||||
|
||||
fn get_table_db(id: u32, func_name: &str) -> APIResult<veilid_core::TableDB> {
|
||||
let table_dbs = TABLE_DBS.lock();
|
||||
let Some(table_db) = table_dbs.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
func_name, "id", id,
|
||||
));
|
||||
};
|
||||
Ok(table_db.clone())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn table_db_get_keys(port: i64, id: u32, col: u32) {
|
||||
DartIsolateWrapper::new(port).spawn_result_json(async move {
|
||||
let table_db = {
|
||||
let table_dbs = TABLE_DBS.lock();
|
||||
let Some(table_db) = table_dbs.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"table_db_get_keys",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
table_db.clone()
|
||||
};
|
||||
let table_db = get_table_db(id, "table_db_get_keys")?;
|
||||
|
||||
let keys = table_db.get_keys(col).await?;
|
||||
let out: Vec<String> = keys
|
||||
@ -1013,20 +962,23 @@ pub extern "C" fn release_table_db_transaction(id: u32) -> i32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn get_table_db_transaction(
|
||||
id: u32,
|
||||
func_name: &str,
|
||||
) -> APIResult<veilid_core::TableDBTransaction> {
|
||||
let tdbts = TABLE_DB_TRANSACTIONS.lock();
|
||||
let Some(tdbt) = tdbts.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
func_name, "id", id,
|
||||
));
|
||||
};
|
||||
Ok(tdbt.clone())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn table_db_transaction_commit(port: i64, id: u32) {
|
||||
DartIsolateWrapper::new(port).spawn_result(async move {
|
||||
let tdbt = {
|
||||
let tdbts = TABLE_DB_TRANSACTIONS.lock();
|
||||
let Some(tdbt) = tdbts.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"table_db_transaction_commit",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
tdbt.clone()
|
||||
};
|
||||
let tdbt = get_table_db_transaction(id, "table_db_transaction_commit")?;
|
||||
|
||||
tdbt.commit().await?;
|
||||
APIRESULT_VOID
|
||||
@ -1035,17 +987,7 @@ pub extern "C" fn table_db_transaction_commit(port: i64, id: u32) {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn table_db_transaction_rollback(port: i64, id: u32) {
|
||||
DartIsolateWrapper::new(port).spawn_result(async move {
|
||||
let tdbt = {
|
||||
let tdbts = TABLE_DB_TRANSACTIONS.lock();
|
||||
let Some(tdbt) = tdbts.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"table_db_transaction_rollback",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
tdbt.clone()
|
||||
};
|
||||
let tdbt = get_table_db_transaction(id, "table_db_transaction_rollback")?;
|
||||
|
||||
tdbt.rollback();
|
||||
APIRESULT_VOID
|
||||
@ -1067,17 +1009,7 @@ pub extern "C" fn table_db_transaction_store(
|
||||
.decode(value.into_opt_string().unwrap().as_bytes())
|
||||
.unwrap();
|
||||
DartIsolateWrapper::new(port).spawn_result(async move {
|
||||
let tdbt = {
|
||||
let tdbts = TABLE_DB_TRANSACTIONS.lock();
|
||||
let Some(tdbt) = tdbts.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"table_db_transaction_store",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
tdbt.clone()
|
||||
};
|
||||
let tdbt = get_table_db_transaction(id, "table_db_transaction_store")?;
|
||||
|
||||
tdbt.store(col, &key, &value)?;
|
||||
APIRESULT_VOID
|
||||
@ -1090,17 +1022,7 @@ pub extern "C" fn table_db_transaction_delete(port: i64, id: u32, col: u32, key:
|
||||
.decode(key.into_opt_string().unwrap().as_bytes())
|
||||
.unwrap();
|
||||
DartIsolateWrapper::new(port).spawn_result(async move {
|
||||
let tdbt = {
|
||||
let tdbts = TABLE_DB_TRANSACTIONS.lock();
|
||||
let Some(tdbt) = tdbts.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"table_db_transaction_delete",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
tdbt.clone()
|
||||
};
|
||||
let tdbt = get_table_db_transaction(id, "table_db_transaction_delete")?;
|
||||
|
||||
tdbt.delete(col, &key)?;
|
||||
APIRESULT_VOID
|
||||
@ -1116,17 +1038,7 @@ pub extern "C" fn table_db_store(port: i64, id: u32, col: u32, key: FfiStr, valu
|
||||
.decode(value.into_opt_string().unwrap().as_bytes())
|
||||
.unwrap();
|
||||
DartIsolateWrapper::new(port).spawn_result(async move {
|
||||
let table_db = {
|
||||
let table_dbs = TABLE_DBS.lock();
|
||||
let Some(table_db) = table_dbs.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"table_db_store",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
table_db.clone()
|
||||
};
|
||||
let table_db = get_table_db(id, "table_db_store")?;
|
||||
|
||||
table_db.store(col, &key, &value).await?;
|
||||
APIRESULT_VOID
|
||||
@ -1139,17 +1051,7 @@ pub extern "C" fn table_db_load(port: i64, id: u32, col: u32, key: FfiStr) {
|
||||
.decode(key.into_opt_string().unwrap().as_bytes())
|
||||
.unwrap();
|
||||
DartIsolateWrapper::new(port).spawn_result(async move {
|
||||
let table_db = {
|
||||
let table_dbs = TABLE_DBS.lock();
|
||||
let Some(table_db) = table_dbs.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"table_db_load",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
table_db.clone()
|
||||
};
|
||||
let table_db = get_table_db(id, "table_db_load")?;
|
||||
|
||||
let out = table_db.load(col, &key).await?;
|
||||
let out = out.map(|x| data_encoding::BASE64URL_NOPAD.encode(&x));
|
||||
@ -1163,17 +1065,7 @@ pub extern "C" fn table_db_delete(port: i64, id: u32, col: u32, key: FfiStr) {
|
||||
.decode(key.into_opt_string().unwrap().as_bytes())
|
||||
.unwrap();
|
||||
DartIsolateWrapper::new(port).spawn_result(async move {
|
||||
let table_db = {
|
||||
let table_dbs = TABLE_DBS.lock();
|
||||
let Some(table_db) = table_dbs.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"table_db_delete",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
table_db.clone()
|
||||
};
|
||||
let table_db = get_table_db(id, "table_db_delete")?;
|
||||
|
||||
let out = table_db.delete(col, &key).await?;
|
||||
let out = out.map(|x| data_encoding::BASE64URL_NOPAD.encode(&x));
|
||||
|
@ -67,7 +67,7 @@ async def test_open_twice_table_db_store_load(api_connection: veilid.VeilidAPI):
|
||||
# store into first db copy
|
||||
await tdb.store(b"asdf", b"1234")
|
||||
# load from second db copy
|
||||
assert await tdb.load(b"asdf") == b"1234"
|
||||
assert await tdb2.load(b"asdf") == b"1234"
|
||||
|
||||
# delete should now succeed
|
||||
deleted = await api_connection.delete_table_db(TEST_DB)
|
||||
|
@ -5,6 +5,9 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
#![no_std]
|
||||
|
||||
/// Veilid WASM Bindings for Flutter/Dart, as well as Native Javascript
|
||||
/// The Flutter/Dart bindings are in this lib.rs directly
|
||||
/// The Native Javascript bindings are in the other files.
|
||||
extern crate alloc;
|
||||
use alloc::string::String;
|
||||
use alloc::sync::Arc;
|
||||
@ -438,20 +441,20 @@ pub fn routing_context_with_sequencing(id: u32, sequencing: String) -> u32 {
|
||||
add_routing_context(routing_context)
|
||||
}
|
||||
|
||||
fn get_routing_context(id: u32, func_name: &str) -> APIResult<veilid_core::RoutingContext> {
|
||||
let rc = (*ROUTING_CONTEXTS).borrow();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
func_name, "id", id,
|
||||
));
|
||||
};
|
||||
Ok(routing_context.clone())
|
||||
}
|
||||
|
||||
#[wasm_bindgen()]
|
||||
pub fn routing_context_safety(id: u32) -> Promise {
|
||||
wrap_api_future_json(async move {
|
||||
let routing_context = {
|
||||
let rc = (*ROUTING_CONTEXTS).borrow();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_safety",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_safety")?;
|
||||
|
||||
let safety_selection = routing_context.safety();
|
||||
APIResult::Ok(safety_selection)
|
||||
@ -464,17 +467,7 @@ pub fn routing_context_app_call(id: u32, target_string: String, request: String)
|
||||
.decode(request.as_bytes())
|
||||
.unwrap();
|
||||
wrap_api_future_plain(async move {
|
||||
let routing_context = {
|
||||
let rc = (*ROUTING_CONTEXTS).borrow();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_app_call",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_app_call")?;
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let target = veilid_api.parse_as_target(target_string).await?;
|
||||
@ -490,17 +483,7 @@ pub fn routing_context_app_message(id: u32, target_string: String, message: Stri
|
||||
.decode(message.as_bytes())
|
||||
.unwrap();
|
||||
wrap_api_future_void(async move {
|
||||
let routing_context = {
|
||||
let rc = (*ROUTING_CONTEXTS).borrow();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_app_message",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_app_message")?;
|
||||
|
||||
let veilid_api = get_veilid_api()?;
|
||||
let target = veilid_api.parse_as_target(target_string).await?;
|
||||
@ -519,17 +502,7 @@ pub fn routing_context_create_dht_record(id: u32, schema: String, kind: u32) ->
|
||||
let schema: veilid_core::DHTSchema = veilid_core::deserialize_json(&schema).unwrap();
|
||||
|
||||
wrap_api_future_json(async move {
|
||||
let routing_context = {
|
||||
let rc = (*ROUTING_CONTEXTS).borrow();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_create_dht_record",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_create_dht_record")?;
|
||||
|
||||
let dht_record_descriptor = routing_context
|
||||
.create_dht_record(schema, crypto_kind)
|
||||
@ -544,17 +517,8 @@ pub fn routing_context_open_dht_record(id: u32, key: String, writer: Option<Stri
|
||||
let writer: Option<veilid_core::KeyPair> =
|
||||
writer.map(|s| veilid_core::deserialize_json(&s).unwrap());
|
||||
wrap_api_future_json(async move {
|
||||
let routing_context = {
|
||||
let rc = (*ROUTING_CONTEXTS).borrow();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_open_dht_record",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_open_dht_record")?;
|
||||
|
||||
let dht_record_descriptor = routing_context.open_dht_record(key, writer).await?;
|
||||
APIResult::Ok(dht_record_descriptor)
|
||||
})
|
||||
@ -564,17 +528,8 @@ pub fn routing_context_open_dht_record(id: u32, key: String, writer: Option<Stri
|
||||
pub fn routing_context_close_dht_record(id: u32, key: String) -> Promise {
|
||||
let key: veilid_core::TypedKey = veilid_core::deserialize_json(&key).unwrap();
|
||||
wrap_api_future_void(async move {
|
||||
let routing_context = {
|
||||
let rc = (*ROUTING_CONTEXTS).borrow();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_close_dht_record",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_close_dht_record")?;
|
||||
|
||||
routing_context.close_dht_record(key).await?;
|
||||
APIRESULT_UNDEFINED
|
||||
})
|
||||
@ -584,17 +539,8 @@ pub fn routing_context_close_dht_record(id: u32, key: String) -> Promise {
|
||||
pub fn routing_context_delete_dht_record(id: u32, key: String) -> Promise {
|
||||
let key: veilid_core::TypedKey = veilid_core::deserialize_json(&key).unwrap();
|
||||
wrap_api_future_void(async move {
|
||||
let routing_context = {
|
||||
let rc = (*ROUTING_CONTEXTS).borrow();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_delete_dht_record",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_delete_dht_record")?;
|
||||
|
||||
routing_context.delete_dht_record(key).await?;
|
||||
APIRESULT_UNDEFINED
|
||||
})
|
||||
@ -609,17 +555,8 @@ pub fn routing_context_get_dht_value(
|
||||
) -> Promise {
|
||||
let key: veilid_core::TypedKey = veilid_core::deserialize_json(&key).unwrap();
|
||||
wrap_api_future_json(async move {
|
||||
let routing_context = {
|
||||
let rc = (*ROUTING_CONTEXTS).borrow();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_get_dht_value",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_get_dht_value")?;
|
||||
|
||||
let res = routing_context
|
||||
.get_dht_value(key, subkey, force_refresh)
|
||||
.await?;
|
||||
@ -643,17 +580,8 @@ pub fn routing_context_set_dht_value(
|
||||
writer.map(|s| veilid_core::deserialize_json(&s).unwrap());
|
||||
|
||||
wrap_api_future_json(async move {
|
||||
let routing_context = {
|
||||
let rc = (*ROUTING_CONTEXTS).borrow();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_set_dht_value",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_set_dht_value")?;
|
||||
|
||||
let res = routing_context
|
||||
.set_dht_value(key, subkey, data, writer)
|
||||
.await?;
|
||||
@ -675,17 +603,8 @@ pub fn routing_context_watch_dht_values(
|
||||
let expiration = veilid_core::Timestamp::from_str(&expiration).unwrap();
|
||||
|
||||
wrap_api_future_plain(async move {
|
||||
let routing_context = {
|
||||
let rc = (*ROUTING_CONTEXTS).borrow();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_watch_dht_values",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_watch_dht_values")?;
|
||||
|
||||
let res = routing_context
|
||||
.watch_dht_values(key, subkeys, expiration, count)
|
||||
.await?;
|
||||
@ -700,17 +619,8 @@ pub fn routing_context_cancel_dht_watch(id: u32, key: String, subkeys: String) -
|
||||
veilid_core::deserialize_json(&subkeys).unwrap();
|
||||
|
||||
wrap_api_future_plain(async move {
|
||||
let routing_context = {
|
||||
let rc = (*ROUTING_CONTEXTS).borrow();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_cancel_dht_watch",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_cancel_dht_watch")?;
|
||||
|
||||
let res = routing_context.cancel_dht_watch(key, subkeys).await?;
|
||||
APIResult::Ok(res)
|
||||
})
|
||||
@ -729,17 +639,8 @@ pub fn routing_context_inspect_dht_record(
|
||||
let scope: veilid_core::DHTReportScope = veilid_core::deserialize_json(&scope).unwrap();
|
||||
|
||||
wrap_api_future_json(async move {
|
||||
let routing_context = {
|
||||
let rc = (*ROUTING_CONTEXTS).borrow();
|
||||
let Some(routing_context) = rc.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"routing_context_inspect_dht_record",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
routing_context.clone()
|
||||
};
|
||||
let routing_context = get_routing_context(id, "routing_context_inspect_dht_record")?;
|
||||
|
||||
let res = routing_context
|
||||
.inspect_dht_record(key, subkeys, scope)
|
||||
.await?;
|
||||
@ -880,20 +781,20 @@ pub fn table_db_get_column_count(id: u32) -> u32 {
|
||||
cc
|
||||
}
|
||||
|
||||
fn get_table_db(id: u32, func_name: &str) -> APIResult<veilid_core::TableDB> {
|
||||
let table_dbs = (*TABLE_DBS).borrow();
|
||||
let Some(table_db) = table_dbs.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
func_name, "id", id,
|
||||
));
|
||||
};
|
||||
Ok(table_db.clone())
|
||||
}
|
||||
|
||||
#[wasm_bindgen()]
|
||||
pub fn table_db_get_keys(id: u32, col: u32) -> Promise {
|
||||
wrap_api_future_json(async move {
|
||||
let table_db = {
|
||||
let table_dbs = (*TABLE_DBS).borrow();
|
||||
let Some(table_db) = table_dbs.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"table_db_store",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
table_db.clone()
|
||||
};
|
||||
let table_db = get_table_db(id, "table_db_get_keys")?;
|
||||
|
||||
let keys = table_db.clone().get_keys(col).await?;
|
||||
let out: Vec<String> = keys
|
||||
@ -933,20 +834,23 @@ pub fn release_table_db_transaction(id: u32) -> i32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn get_table_db_transaction(
|
||||
id: u32,
|
||||
func_name: &str,
|
||||
) -> APIResult<veilid_core::TableDBTransaction> {
|
||||
let tdbts = (*TABLE_DB_TRANSACTIONS).borrow();
|
||||
let Some(tdbt) = tdbts.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
func_name, "id", id,
|
||||
));
|
||||
};
|
||||
Ok(tdbt.clone())
|
||||
}
|
||||
|
||||
#[wasm_bindgen()]
|
||||
pub fn table_db_transaction_commit(id: u32) -> Promise {
|
||||
wrap_api_future_void(async move {
|
||||
let tdbt = {
|
||||
let tdbts = (*TABLE_DB_TRANSACTIONS).borrow();
|
||||
let Some(tdbt) = tdbts.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"table_db_transaction_commit",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
tdbt.clone()
|
||||
};
|
||||
let tdbt = get_table_db_transaction(id, "table_db_transaction_commit")?;
|
||||
|
||||
tdbt.commit().await?;
|
||||
APIRESULT_UNDEFINED
|
||||
@ -956,17 +860,7 @@ pub fn table_db_transaction_commit(id: u32) -> Promise {
|
||||
#[wasm_bindgen()]
|
||||
pub fn table_db_transaction_rollback(id: u32) -> Promise {
|
||||
wrap_api_future_void(async move {
|
||||
let tdbt = {
|
||||
let tdbts = (*TABLE_DB_TRANSACTIONS).borrow();
|
||||
let Some(tdbt) = tdbts.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"table_db_transaction_rollback",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
tdbt.clone()
|
||||
};
|
||||
let tdbt = get_table_db_transaction(id, "table_db_transaction_rollback")?;
|
||||
|
||||
tdbt.rollback();
|
||||
APIRESULT_UNDEFINED
|
||||
@ -982,17 +876,7 @@ pub fn table_db_transaction_store(id: u32, col: u32, key: String, value: String)
|
||||
.decode(value.as_bytes())
|
||||
.unwrap();
|
||||
wrap_api_future_void(async move {
|
||||
let tdbt = {
|
||||
let tdbts = (*TABLE_DB_TRANSACTIONS).borrow();
|
||||
let Some(tdbt) = tdbts.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"table_db_transaction_store",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
tdbt.clone()
|
||||
};
|
||||
let tdbt = get_table_db_transaction(id, "table_db_transaction_store")?;
|
||||
|
||||
tdbt.store(col, &key, &value)?;
|
||||
APIRESULT_UNDEFINED
|
||||
@ -1005,17 +889,7 @@ pub fn table_db_transaction_delete(id: u32, col: u32, key: String) -> Promise {
|
||||
.decode(key.as_bytes())
|
||||
.unwrap();
|
||||
wrap_api_future_void(async move {
|
||||
let tdbt = {
|
||||
let tdbts = (*TABLE_DB_TRANSACTIONS).borrow();
|
||||
let Some(tdbt) = tdbts.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"table_db_transaction_delete",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
tdbt.clone()
|
||||
};
|
||||
let tdbt = get_table_db_transaction(id, "table_db_transaction_delete")?;
|
||||
|
||||
tdbt.delete(col, &key)?;
|
||||
APIRESULT_UNDEFINED
|
||||
@ -1031,17 +905,7 @@ pub fn table_db_store(id: u32, col: u32, key: String, value: String) -> Promise
|
||||
.decode(value.as_bytes())
|
||||
.unwrap();
|
||||
wrap_api_future_void(async move {
|
||||
let table_db = {
|
||||
let table_dbs = (*TABLE_DBS).borrow();
|
||||
let Some(table_db) = table_dbs.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"table_db_store",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
table_db.clone()
|
||||
};
|
||||
let table_db = get_table_db(id, "table_db_store")?;
|
||||
|
||||
table_db.store(col, &key, &value).await?;
|
||||
APIRESULT_UNDEFINED
|
||||
@ -1054,17 +918,7 @@ pub fn table_db_load(id: u32, col: u32, key: String) -> Promise {
|
||||
.decode(key.as_bytes())
|
||||
.unwrap();
|
||||
wrap_api_future_plain(async move {
|
||||
let table_db = {
|
||||
let table_dbs = (*TABLE_DBS).borrow();
|
||||
let Some(table_db) = table_dbs.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"table_db_load",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
table_db.clone()
|
||||
};
|
||||
let table_db = get_table_db(id, "table_db_load")?;
|
||||
|
||||
let out = table_db.load(col, &key).await?;
|
||||
let out = out.map(|x| data_encoding::BASE64URL_NOPAD.encode(&x));
|
||||
@ -1078,17 +932,7 @@ pub fn table_db_delete(id: u32, col: u32, key: String) -> Promise {
|
||||
.decode(key.as_bytes())
|
||||
.unwrap();
|
||||
wrap_api_future_plain(async move {
|
||||
let table_db = {
|
||||
let table_dbs = (*TABLE_DBS).borrow();
|
||||
let Some(table_db) = table_dbs.get(&id) else {
|
||||
return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(
|
||||
"table_db_delete",
|
||||
"id",
|
||||
id,
|
||||
));
|
||||
};
|
||||
table_db.clone()
|
||||
};
|
||||
let table_db = get_table_db(id, "table_db_delete")?;
|
||||
|
||||
let out = table_db.delete(col, &key).await?;
|
||||
let out = out.map(|x| data_encoding::BASE64URL_NOPAD.encode(&x));
|
||||
|
Loading…
Reference in New Issue
Block a user