Skip to content

Commit 4b7459c

Browse files
authoredNov 21, 2024
[hotfix] resource names (#302)
1 parent 68f593b commit 4b7459c

File tree

10 files changed

+181
-23
lines changed

10 files changed

+181
-23
lines changed
 

‎example/viam_example_app/ios/Flutter/AppFrameworkInfo.plist

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@
2121
<key>CFBundleVersion</key>
2222
<string>1.0</string>
2323
<key>MinimumOSVersion</key>
24-
<string>11.0</string>
24+
<string>12.0</string>
2525
</dict>
2626
</plist>

‎example/viam_example_app/ios/Runner.xcodeproj/project.pbxproj

+4-4
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@
216216
isa = PBXProject;
217217
attributes = {
218218
BuildIndependentTargetsInParallel = YES;
219-
LastUpgradeCheck = 1430;
219+
LastUpgradeCheck = 1510;
220220
ORGANIZATIONNAME = "";
221221
TargetAttributes = {
222222
331C8080294A63A400263BE5 = {
@@ -453,7 +453,7 @@
453453
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
454454
GCC_WARN_UNUSED_FUNCTION = YES;
455455
GCC_WARN_UNUSED_VARIABLE = YES;
456-
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
456+
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
457457
MTL_ENABLE_DEBUG_INFO = NO;
458458
SDKROOT = iphoneos;
459459
SUPPORTED_PLATFORMS = iphoneos;
@@ -580,7 +580,7 @@
580580
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
581581
GCC_WARN_UNUSED_FUNCTION = YES;
582582
GCC_WARN_UNUSED_VARIABLE = YES;
583-
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
583+
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
584584
MTL_ENABLE_DEBUG_INFO = YES;
585585
ONLY_ACTIVE_ARCH = YES;
586586
SDKROOT = iphoneos;
@@ -629,7 +629,7 @@
629629
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
630630
GCC_WARN_UNUSED_FUNCTION = YES;
631631
GCC_WARN_UNUSED_VARIABLE = YES;
632-
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
632+
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
633633
MTL_ENABLE_DEBUG_INFO = NO;
634634
SDKROOT = iphoneos;
635635
SUPPORTED_PLATFORMS = iphoneos;

‎example/viam_example_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1430"
3+
LastUpgradeVersion = "1510"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"

‎example/viam_example_app/ios/Runner/AppDelegate.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import UIKit
22
import Flutter
33

4-
@UIApplicationMain
4+
@main
55
@objc class AppDelegate: FlutterAppDelegate {
66
override func application(
77
_ application: UIApplication,

‎lib/src/exceptions.dart

+29
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import '../protos/common/common.dart';
2+
13
/// Error thrown with connection is lost
24
class ConnectionLostException implements Exception {
35
final String? message;
@@ -17,3 +19,30 @@ class NotLocalAddressException implements Exception {
1719
@override
1820
String toString() => 'Address not on local network';
1921
}
22+
23+
class DuplicateResourceException implements Exception {
24+
final ResourceName name;
25+
26+
const DuplicateResourceException(this.name);
27+
28+
@override
29+
String toString() => 'Duplicate registration of resource in manager: $name';
30+
}
31+
32+
class ResourceNotFoundException implements Exception {
33+
final ResourceName name;
34+
35+
const ResourceNotFoundException(this.name);
36+
37+
@override
38+
String toString() => 'Resource not found in manager: $name';
39+
}
40+
41+
class MultipleRemoteResourcesSameNameException implements Exception {
42+
final Iterable<ResourceName> names;
43+
44+
const MultipleRemoteResourcesSameNameException(this.names);
45+
46+
@override
47+
String toString() => 'Multiple remote resources found with the name ${names.first.localName}: $names';
48+
}

‎lib/src/resource/base.dart

+3-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ class Subtype {
5151
..namespace = namespace
5252
..type = resourceType
5353
..subtype = resourceSubtype
54-
..name = name;
54+
..remotePath.addAll(name.split(':')..removeLast())
55+
..name = name
56+
..localName = name.split(':').last;
5557
}
5658

5759
@override

‎lib/src/resource/manager.dart

+26-14
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,47 @@
1+
import 'package:protobuf/protobuf.dart';
2+
3+
import '../exceptions.dart';
14
import '../gen/common/v1/common.pb.dart';
25
import 'base.dart';
36

47
/// [ResourceManager] manages the state of all currently available resources of a robot
58
class ResourceManager {
69
/// The available resources
710
Map<ResourceName, Resource> resources = {};
8-
final Map<String, List<ResourceName>> _shortToLongName = {};
11+
final Map<ResourceName, List<ResourceName>> _resourceNamesWithoutRemotes = {};
912

1013
/// Register a new [Resource] with the manager.
1114
void register(ResourceName name, Resource resource) {
1215
if (resources.containsKey(name)) {
13-
throw Exception('Duplicate registration of resource in manager');
16+
throw DuplicateResourceException(name);
1417
}
15-
final shortName = name.name.split(':').last;
16-
final names = _shortToLongName[shortName] ?? [];
18+
final rnWithoutRemote = name.deepCopy()
19+
..remotePath.clear()
20+
..name = name.localName;
21+
final names = _resourceNamesWithoutRemotes[rnWithoutRemote] ?? [];
1722
names.add(name);
18-
_shortToLongName[shortName] = names;
23+
_resourceNamesWithoutRemotes[rnWithoutRemote] = names;
1924
resources[name] = resource;
2025
}
2126

2227
/// Get a resource with the given [ResourceName]
2328
T getResource<T>(ResourceName name) {
24-
final resource = resources[name];
25-
if (resource == null) throw Exception('Resource not found in manager');
29+
Resource? resource;
30+
if (resources.containsKey(name)) {
31+
resource = resources[name];
32+
} else {
33+
final resourceNames = _resourceNamesWithoutRemotes[name] ?? [];
34+
// If multiple ResourceNames map to this name-without-remotes,
35+
// that means there are multiple remote resources with this same short name.
36+
// Without any means to disambiguate, we should not select any.
37+
if (resourceNames.length > 1) {
38+
throw MultipleRemoteResourcesSameNameException(resourceNames);
39+
}
40+
if (resourceNames.length == 1) {
41+
resource = resources[resourceNames.first];
42+
}
43+
}
44+
if (resource == null) throw ResourceNotFoundException(name);
2645
return resource as T;
2746
}
28-
29-
/// Get a resource by its name only
30-
T getResourceByName<T>(String name) {
31-
final names = _shortToLongName[name] ?? [];
32-
if (names.isEmpty) throw Exception('Resource not found in manager');
33-
return getResource(names.first);
34-
}
3547
}

‎pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ environment:
1010
dependencies:
1111
flutter:
1212
sdk: flutter
13-
flutter_webrtc: ^0.11.0
13+
flutter_webrtc: ^0.12.1+hotfix.1
1414
grpc: ^4.0.1
1515
protobuf: ^3.0.0
1616
image: ^4.0.16
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import 'package:flutter_test/flutter_test.dart';
2+
import 'package:viam_sdk/src/components/sensor/sensor.dart';
3+
4+
void main() {
5+
group('Subtype Tests', () {
6+
test('getResourceName', () {
7+
// Local
8+
final localName = 'my-sensor';
9+
final localRN = Sensor.subtype.getResourceName(localName);
10+
expect(localRN.namespace, Sensor.subtype.namespace);
11+
expect(localRN.type, Sensor.subtype.resourceType);
12+
expect(localRN.subtype, Sensor.subtype.resourceSubtype);
13+
expect(localRN.remotePath, []);
14+
expect(localRN.name, localName);
15+
expect(localRN.localName, localName);
16+
17+
// Remote
18+
final remoteName = 'my-sensor';
19+
final remotePath = 'remote2:remote1';
20+
final fullRemoteName = '$remotePath:$remoteName';
21+
final remoteRN = Sensor.subtype.getResourceName(fullRemoteName);
22+
expect(remoteRN.namespace, Sensor.subtype.namespace);
23+
expect(remoteRN.type, Sensor.subtype.resourceType);
24+
expect(remoteRN.subtype, Sensor.subtype.resourceSubtype);
25+
expect(remoteRN.remotePath, remotePath.split(':'));
26+
expect(remoteRN.name, fullRemoteName);
27+
expect(remoteRN.localName, remoteName);
28+
});
29+
});
30+
}
+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import 'package:flutter_test/flutter_test.dart';
2+
import 'package:viam_sdk/src/components/sensor/sensor.dart';
3+
import 'package:viam_sdk/src/exceptions.dart';
4+
import 'package:viam_sdk/src/resource/manager.dart';
5+
6+
import '../components/sensor_test.dart';
7+
8+
void main() {
9+
group('ResourceManager Tests', () {
10+
group('getResource', () {
11+
test('Local', () {
12+
final localName = 'local-sensor';
13+
final localRN = Sensor.getResourceName(localName);
14+
final localResource = FakeSensor(localName);
15+
final manager = ResourceManager();
16+
manager.register(localRN, localResource);
17+
18+
expect(manager.getResource<Sensor>(Sensor.getResourceName(localName)), localResource);
19+
});
20+
21+
test('Remote', () {
22+
final remoteName = 'remote-sensor';
23+
final remotePath = 'remote2:remote1';
24+
final fullRemoteName = '$remotePath:$remoteName';
25+
final remoteRN = Sensor.subtype.getResourceName(fullRemoteName);
26+
final remoteResource = FakeSensor(fullRemoteName);
27+
final manager = ResourceManager();
28+
manager.register(remoteRN, remoteResource);
29+
30+
// Works with full name
31+
expect(manager.getResource<Sensor>(Sensor.getResourceName(fullRemoteName)), remoteResource);
32+
33+
// Works with short name -- no collisions
34+
expect(manager.getResource<Sensor>(Sensor.getResourceName(remoteName)), remoteResource);
35+
});
36+
37+
test('Local and Remote - Same Names', () {
38+
final manager = ResourceManager();
39+
40+
final localName = 'my-sensor';
41+
final localRN = Sensor.getResourceName(localName);
42+
final localResource = FakeSensor(localName);
43+
44+
final remoteName = 'my-sensor';
45+
final remotePath = 'remote2:remote1';
46+
final fullRemoteName = '$remotePath:$remoteName';
47+
final remoteRN = Sensor.subtype.getResourceName(fullRemoteName);
48+
final remoteResource = FakeSensor(fullRemoteName);
49+
50+
manager.register(localRN, localResource);
51+
manager.register(remoteRN, remoteResource);
52+
53+
// When using fully qualified names, it should return appropriately
54+
expect(manager.getResource<Sensor>(Sensor.getResourceName(localName)), localResource);
55+
expect(manager.getResource<Sensor>(Sensor.getResourceName(fullRemoteName)), remoteResource);
56+
57+
// When using just `my-sensor`, it should always return the local
58+
expect(manager.getResource<Sensor>(Sensor.getResourceName(localName)), localResource);
59+
expect(manager.getResource<Sensor>(Sensor.getResourceName(remoteName)), localResource);
60+
});
61+
62+
test('Multiple Remotes - Same Names', () {
63+
final remoteName = 'my-sensor';
64+
65+
final remotePath1 = 'remote2:remote1';
66+
final fullRemoteName1 = '$remotePath1:$remoteName';
67+
final remoteRN1 = Sensor.subtype.getResourceName(fullRemoteName1);
68+
final remoteResource1 = FakeSensor(fullRemoteName1);
69+
70+
final remotePath2 = 'remote4:remote3';
71+
final fullRemoteName2 = '$remotePath2:$remoteName';
72+
final remoteRN2 = Sensor.subtype.getResourceName(fullRemoteName2);
73+
final remoteResource2 = FakeSensor(fullRemoteName2);
74+
75+
final manager = ResourceManager();
76+
manager.register(remoteRN1, remoteResource1);
77+
manager.register(remoteRN2, remoteResource2);
78+
79+
// Error when using short name only
80+
expect(() => manager.getResource<Sensor>(Sensor.getResourceName(remoteName)),
81+
throwsA(isA<MultipleRemoteResourcesSameNameException>()));
82+
});
83+
});
84+
});
85+
}

0 commit comments

Comments
 (0)
Please sign in to comment.