Skip to content

Commit f73a2ef

Browse files
authored
[RSDK-10139] Implement User-defined Metadata Methods (#882)
1 parent be703ba commit f73a2ef

File tree

3 files changed

+253
-0
lines changed

3 files changed

+253
-0
lines changed

src/viam/app/app_client.py

+148
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,16 @@
5454
GetFragmentResponse,
5555
GetLocationRequest,
5656
GetLocationResponse,
57+
GetLocationMetadataRequest,
58+
GetLocationMetadataResponse,
5759
GetModuleRequest,
5860
GetModuleResponse,
5961
GetOrganizationNamespaceAvailabilityRequest,
6062
GetOrganizationNamespaceAvailabilityResponse,
6163
GetOrganizationRequest,
6264
GetOrganizationResponse,
65+
GetOrganizationMetadataRequest,
66+
GetOrganizationMetadataResponse,
6367
GetOrganizationsWithAccessToLocationRequest,
6468
GetOrganizationsWithAccessToLocationResponse,
6569
GetRegistryItemRequest,
@@ -70,12 +74,16 @@
7074
GetRobotPartHistoryResponse,
7175
GetRobotPartLogsRequest,
7276
GetRobotPartLogsResponse,
77+
GetRobotPartMetadataRequest,
78+
GetRobotPartMetadataResponse,
7379
GetRobotPartRequest,
7480
GetRobotPartResponse,
7581
GetRobotPartsRequest,
7682
GetRobotPartsResponse,
7783
GetRobotRequest,
7884
GetRobotResponse,
85+
GetRobotMetadataRequest,
86+
GetRobotMetadataResponse,
7987
GetRoverRentalRobotsRequest,
8088
GetRoverRentalRobotsResponse,
8189
GetUserIDByEmailRequest,
@@ -134,15 +142,23 @@
134142
UnshareLocationRequest,
135143
UpdateFragmentRequest,
136144
UpdateFragmentResponse,
145+
UpdateLocationMetadataRequest,
146+
UpdateLocationMetadataResponse,
137147
UpdateLocationRequest,
138148
UpdateLocationResponse,
139149
UpdateModuleRequest,
140150
UpdateModuleResponse,
141151
UpdateOrganizationInviteAuthorizationsRequest,
142152
UpdateOrganizationInviteAuthorizationsResponse,
153+
UpdateOrganizationMetadataRequest,
154+
UpdateOrganizationMetadataResponse,
143155
UpdateOrganizationRequest,
144156
UpdateOrganizationResponse,
145157
UpdateRegistryItemRequest,
158+
UpdateRobotMetadataRequest,
159+
UpdateRobotMetadataResponse,
160+
UpdateRobotPartMetadataRequest,
161+
UpdateRobotPartMetadataResponse,
146162
UpdateRobotPartRequest,
147163
UpdateRobotPartResponse,
148164
UpdateRobotRequest,
@@ -2523,3 +2539,135 @@ async def rotate_key(self, id: str) -> Tuple[str, str]:
25232539
request = RotateKeyRequest(id=id)
25242540
response: RotateKeyResponse = await self._app_client.RotateKey(request, metadata=self._metadata)
25252541
return response.key, response.id
2542+
2543+
async def get_organization_metadata(self, org_id: str) -> Mapping[str, Any]:
2544+
"""Get an organization's user-defined metadata.
2545+
2546+
::
2547+
2548+
metadata = await cloud.get_organization_metadata(org_id="<YOUR-ORG-ID>")
2549+
2550+
Args:
2551+
org_id (str): The ID of the organization with which the user-defined metadata is associated.
2552+
You can obtain your organization ID from the Viam app's organization settings page.
2553+
2554+
Returns:
2555+
Mapping[str, Any]: The user-defined metadata converted from JSON to a Python dictionary
2556+
"""
2557+
request = GetOrganizationMetadataRequest(organization_id=org_id)
2558+
response: GetOrganizationMetadataResponse = await self._app_client.GetOrganizationMetadata(request)
2559+
return struct_to_dict(response.data)
2560+
2561+
async def update_organization_metadata(self, org_id: str, metadata: Mapping[str, Any]) -> None:
2562+
"""Update an organization's user-defined metadata.
2563+
2564+
::
2565+
2566+
await cloud.update_organization_metadata(org_id="<YOUR-ORG-ID>", metadata=)
2567+
2568+
Args:
2569+
organization_id (str): The ID of the organization with which to associate the user-defined metadata.
2570+
You can obtain your organization ID from the Viam app's organization settings page.
2571+
metadata (Mapping[str, Any]): The user-defined metadata to upload as a Python dictionary.
2572+
"""
2573+
request = UpdateOrganizationMetadataRequest(organization_id=org_id, data=dict_to_struct(metadata))
2574+
_: UpdateOrganizationMetadataResponse = await self._app_client.UpdateOrganizationMetadata(request)
2575+
2576+
async def get_location_metadata(self, location_id: str) -> Mapping[str, Any]:
2577+
"""Get a location's user-defined metadata.
2578+
2579+
::
2580+
2581+
metadata = await cloud.get_location_metadata(location_id="<YOUR-LOCATION-ID>")
2582+
2583+
Args:
2584+
location_id (str): The ID of the location with which the user-defined metadata is associated.
2585+
You can obtain your location ID from the Viam app's locations page.
2586+
2587+
Returns:
2588+
Mapping[str, Any]: The user-defined metadata converted from JSON to a Python dictionary.
2589+
"""
2590+
request = GetLocationMetadataRequest(location_id=location_id)
2591+
response: GetLocationMetadataResponse = await self._app_client.GetLocationMetadata(request)
2592+
return struct_to_dict(response.data)
2593+
2594+
async def update_location_metadata(self, location_id: str, metadata: Mapping[str, Any]) -> None:
2595+
"""Update a location's user-defined metadata.
2596+
2597+
::
2598+
2599+
await cloud.update_location_metadata(location_id="<YOUR-LOCATION-ID>", metadata=)
2600+
2601+
Args:
2602+
location_id (str): The ID of the location with which to associate the user-defined metadata.
2603+
You can obtain your location ID from the Viam app's locations page.
2604+
metadata (Mapping[str, Any]): The user-defined metadata converted from JSON to a Python dictionary.
2605+
"""
2606+
request = UpdateLocationMetadataRequest(location_id=location_id, data=dict_to_struct(metadata))
2607+
_: UpdateLocationMetadataResponse = await self._app_client.UpdateLocationMetadata(request)
2608+
2609+
async def get_robot_metadata(self, robot_id: str) -> Mapping[str, Any]:
2610+
"""Get a robot's user-defined metadata.
2611+
2612+
::
2613+
2614+
metadata = await cloud.get_robot_metadata(robot_id="<YOUR-ROBOT-ID>")
2615+
2616+
Args:
2617+
robot_id (str): The ID of the robot with which the user-defined metadata is associated.
2618+
You can obtain your robot ID from the Viam app's machine page.
2619+
2620+
Returns:
2621+
Mapping[str, Any]: The user-defined metadata converted from JSON to a Python dictionary.
2622+
"""
2623+
request = GetRobotMetadataRequest(id=robot_id)
2624+
response: GetRobotMetadataResponse = await self._app_client.GetRobotMetadata(request)
2625+
return struct_to_dict(response.data)
2626+
2627+
async def update_robot_metadata(self, robot_id: str, metadata: Mapping[str, Any]) -> None:
2628+
"""Update a robot's user-defined metadata.
2629+
2630+
::
2631+
2632+
await cloud.update_robot_metadata(robot_id="<YOUR-ROBOT-ID>", metadata=)
2633+
2634+
Args:
2635+
robot_id (str): The ID of the robot with which to associate the user-defined metadata.
2636+
You can obtain your robot ID from the Viam app's machine page.
2637+
metadata (Mapping[str, Any]): The user-defined metadata converted from JSON to a Python dictionary.
2638+
"""
2639+
request = UpdateRobotMetadataRequest(id=robot_id, data=dict_to_struct(metadata))
2640+
_: UpdateRobotMetadataResponse = await self._app_client.UpdateRobotMetadata(request)
2641+
2642+
async def get_robot_part_metadata(self, robot_part_id: str) -> Mapping[str, Any]:
2643+
"""Get a robot part's user-defined metadata.
2644+
2645+
::
2646+
2647+
metadata = await cloud.get_robot_part_metadata(robot_part_id="<YOUR-ROBOT-PART-ID>")
2648+
2649+
Args:
2650+
robot_part_id (str): The ID of the robot part with which the user-defined metadata is associated.
2651+
You can obtain your robot part ID from the Viam app's machine page.
2652+
2653+
Returns:
2654+
Mapping[str, Any]: The user-defined metadata converted from JSON to a Python dictionary.
2655+
"""
2656+
request = GetRobotPartMetadataRequest(id=robot_part_id)
2657+
response: GetRobotPartMetadataResponse = await self._app_client.GetRobotPartMetadata(request)
2658+
return struct_to_dict(response.data)
2659+
2660+
async def update_robot_part_metadata(self, robot_part_id: str, metadata: Mapping[str, Any]) -> None:
2661+
"""Update a robot part's user-defined metadata.
2662+
2663+
::
2664+
2665+
await cloud.update_robot_part_metadata(robot_part_id="<YOUR-ROBOT-PART-ID>", metadata=)
2666+
2667+
Args:
2668+
robot_id (str): The ID of the robot part with which to associate the user-defined metadata.
2669+
You can obtain your robot part ID from the Viam app's machine page.
2670+
metadata (Mapping[str, Any]): The user-defined metadata converted from JSON to a Python dictionary.
2671+
"""
2672+
request = UpdateRobotPartMetadataRequest(id=robot_part_id, data=dict_to_struct(metadata))
2673+
_: UpdateRobotPartMetadataResponse = await self._app_client.UpdateRobotPartMetadata(request)

tests/mocks/services.py

+63
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
Fragment,
6464
GetFragmentRequest,
6565
GetFragmentResponse,
66+
GetLocationMetadataRequest,
67+
GetLocationMetadataResponse,
6668
GetLocationRequest,
6769
GetLocationResponse,
6870
GetModuleRequest,
@@ -71,16 +73,22 @@
7173
GetOrganizationNamespaceAvailabilityResponse,
7274
GetOrganizationRequest,
7375
GetOrganizationResponse,
76+
GetOrganizationMetadataRequest,
77+
GetOrganizationMetadataResponse,
7478
GetOrganizationsWithAccessToLocationRequest,
7579
GetOrganizationsWithAccessToLocationResponse,
7680
GetRegistryItemRequest,
7781
GetRegistryItemResponse,
7882
GetRobotAPIKeysRequest,
7983
GetRobotAPIKeysResponse,
84+
GetRobotMetadataRequest,
85+
GetRobotMetadataResponse,
8086
GetRobotPartHistoryRequest,
8187
GetRobotPartHistoryResponse,
8288
GetRobotPartLogsRequest,
8389
GetRobotPartLogsResponse,
90+
GetRobotPartMetadataRequest,
91+
GetRobotPartMetadataResponse,
8492
GetRobotPartRequest,
8593
GetRobotPartResponse,
8694
GetRobotPartsRequest,
@@ -149,16 +157,24 @@
149157
UnshareLocationResponse,
150158
UpdateFragmentRequest,
151159
UpdateFragmentResponse,
160+
UpdateLocationMetadataRequest,
161+
UpdateLocationMetadataResponse,
152162
UpdateLocationRequest,
153163
UpdateLocationResponse,
154164
UpdateModuleRequest,
155165
UpdateModuleResponse,
156166
UpdateOrganizationInviteAuthorizationsRequest,
157167
UpdateOrganizationInviteAuthorizationsResponse,
168+
UpdateOrganizationMetadataRequest,
169+
UpdateOrganizationMetadataResponse,
158170
UpdateOrganizationRequest,
159171
UpdateOrganizationResponse,
160172
UpdateRegistryItemRequest,
161173
UpdateRegistryItemResponse,
174+
UpdateRobotMetadataRequest,
175+
UpdateRobotMetadataResponse,
176+
UpdateRobotPartMetadataRequest,
177+
UpdateRobotPartMetadataResponse,
162178
UpdateRobotPartRequest,
163179
UpdateRobotPartResponse,
164180
UpdateRobotRequest,
@@ -1240,6 +1256,10 @@ def __init__(
12401256
self.items = items
12411257
self.package_type = package_type
12421258
self.send_email_invite = False
1259+
self.organization_metadata = {}
1260+
self.location_metadata = {}
1261+
self.robot_metadata = {}
1262+
self.robot_part_metadata = {}
12431263

12441264
async def GetUserIDByEmail(self, stream: Stream[GetUserIDByEmailRequest, GetUserIDByEmailResponse]) -> None:
12451265
request = await stream.recv_message()
@@ -1701,6 +1721,49 @@ async def GetRegistryItem(self, stream: Stream[GetRegistryItemRequest, GetRegist
17011721
self.include_markdown_documentation = request.include_markdown_documentation
17021722
await stream.send_message(GetRegistryItemResponse(item=self.items[0]))
17031723

1724+
async def GetOrganizationMetadata(self, stream: Stream[GetOrganizationMetadataRequest, GetOrganizationMetadataResponse]) -> None:
1725+
request = await stream.recv_message()
1726+
assert request is not None
1727+
await stream.send_message(GetOrganizationMetadataResponse(data=self.organization_metadata.get(request.organization_id, dict_to_struct({}))))
1728+
1729+
async def UpdateOrganizationMetadata(self, stream: Stream[UpdateOrganizationMetadataRequest, UpdateOrganizationMetadataResponse]) -> None:
1730+
request = await stream.recv_message()
1731+
assert request is not None
1732+
self.organization_metadata[request.organization_id] = request.data
1733+
await stream.send_message(UpdateOrganizationMetadataResponse())
1734+
1735+
async def GetLocationMetadata(self, stream: Stream[GetLocationMetadataRequest, GetLocationMetadataResponse]) -> None:
1736+
request = await stream.recv_message()
1737+
assert request is not None
1738+
await stream.send_message(GetLocationMetadataResponse(data=self.location_metadata.get(request.location_id, dict_to_struct({}))))
1739+
1740+
async def UpdateLocationMetadata(self, stream: Stream[UpdateLocationMetadataRequest, UpdateLocationMetadataResponse]) -> None:
1741+
request = await stream.recv_message()
1742+
assert request is not None
1743+
self.location_metadata[request.location_id] = request.data
1744+
await stream.send_message(UpdateLocationMetadataResponse())
1745+
1746+
async def GetRobotMetadata(self, stream: Stream[GetRobotMetadataRequest, GetRobotMetadataResponse]) -> None:
1747+
request = await stream.recv_message()
1748+
assert request is not None
1749+
await stream.send_message(GetRobotMetadataResponse(data=self.robot_metadata.get(request.id, dict_to_struct({}))))
1750+
1751+
async def UpdateRobotMetadata(self, stream: Stream[UpdateRobotMetadataRequest, UpdateRobotMetadataResponse]) -> None:
1752+
request = await stream.recv_message()
1753+
assert request is not None
1754+
self.robot_metadata[request.id] = request.data
1755+
await stream.send_message(UpdateRobotMetadataResponse())
1756+
1757+
async def GetRobotPartMetadata(self, stream: Stream[GetRobotPartMetadataRequest, GetRobotPartMetadataResponse]) -> None:
1758+
request = await stream.recv_message()
1759+
assert request is not None
1760+
await stream.send_message(GetRobotPartMetadataResponse(data=self.robot_part_metadata.get(request.id, dict_to_struct({}))))
1761+
1762+
async def UpdateRobotPartMetadata(self, stream: Stream[UpdateRobotPartMetadataRequest, UpdateRobotPartMetadataResponse]) -> None:
1763+
request = await stream.recv_message()
1764+
assert request is not None
1765+
self.robot_part_metadata[request.id] = request.data
1766+
await stream.send_message(UpdateRobotPartMetadataResponse())
17041767

17051768
class MockGenericService(GenericService):
17061769
timeout: Optional[float] = None

tests/test_app_client.py

+42
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@
205205
PLATFORM = "platform"
206206
MODULE_FILE_INFO = ModuleFileInfo(module_id=ID, version=VERSION, platform=PLATFORM)
207207
FILE = b"file"
208+
USER_DEFINED_METADATA = {"number": 0, "string": "string"}
208209

209210

210211
@pytest.fixture(scope="function")
@@ -778,3 +779,44 @@ async def test_rotate_key(self, service: MockApp):
778779
key, id = await client.rotate_key(ID)
779780
assert key == API_KEY
780781
assert id == ID
782+
783+
async def test_get_and_update_organization_metadata(self, service: MockApp):
784+
async with ChannelFor([service]) as channel:
785+
client = AppClient(channel, METADATA, ID)
786+
user_defined_metadata = await client.get_organization_metadata(ID)
787+
assert len(user_defined_metadata) == 0
788+
789+
await client.update_organization_metadata(ID, USER_DEFINED_METADATA)
790+
user_defined_metadata = await client.get_organization_metadata(ID)
791+
assert user_defined_metadata == USER_DEFINED_METADATA
792+
793+
async def test_get_and_update_location_metadata(self, service: MockApp):
794+
async with ChannelFor([service]) as channel:
795+
client = AppClient(channel, METADATA, ID)
796+
user_defined_metadata = await client.get_location_metadata(ID)
797+
assert len(user_defined_metadata) == 0
798+
799+
800+
await client.update_location_metadata(ID, USER_DEFINED_METADATA)
801+
user_defined_metadata = await client.get_location_metadata(ID)
802+
assert user_defined_metadata == USER_DEFINED_METADATA
803+
804+
async def test_get_and_update_robot_metadata(self, service: MockApp):
805+
async with ChannelFor([service]) as channel:
806+
client = AppClient(channel, METADATA, ID)
807+
user_defined_metadata = await client.get_robot_metadata(ID)
808+
assert len(user_defined_metadata) == 0
809+
810+
await client.update_robot_metadata(ID, USER_DEFINED_METADATA)
811+
user_defined_metadata = await client.get_robot_metadata(ID)
812+
assert user_defined_metadata == USER_DEFINED_METADATA
813+
814+
async def test_get_and_update_robot_part_metadata(self, service: MockApp):
815+
async with ChannelFor([service]) as channel:
816+
client = AppClient(channel, METADATA, ID)
817+
user_defined_metadata = await client.get_robot_part_metadata(ID)
818+
assert len(user_defined_metadata) == 0
819+
820+
await client.update_robot_part_metadata(ID, USER_DEFINED_METADATA)
821+
user_defined_metadata = await client.get_robot_part_metadata(ID)
822+
assert user_defined_metadata == USER_DEFINED_METADATA

0 commit comments

Comments
 (0)