Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions python/lib/sift_client/_tests/util/test_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from sift.metadata.v1.metadata_pb2 import (
MetadataKey,
MetadataKeyType,
MetadataValue,
)

from sift_client.util.metadata import metadata_dict_to_proto, metadata_proto_to_dict


def _string_value(name: str, value: str) -> MetadataValue:
return MetadataValue(
key=MetadataKey(name=name, type=MetadataKeyType.METADATA_KEY_TYPE_STRING),
string_value=value,
)


class TestMetadataProtoToDict:
def test_unwraps_each_value_type(self):
metadata = [
_string_value("env", "prod"),
MetadataValue(
key=MetadataKey(name="build", type=MetadataKeyType.METADATA_KEY_TYPE_NUMBER),
number_value=1.5,
),
MetadataValue(
key=MetadataKey(name="armed", type=MetadataKeyType.METADATA_KEY_TYPE_BOOLEAN),
boolean_value=True,
),
]
assert metadata_proto_to_dict(metadata) == {
"env": "prod",
"build": 1.5,
"armed": True,
}

def test_multi_value_key_keeps_first_value(self):
# A key can hold multiple values (multi-value metadata). The API
# returns values in canonical order; the dict view keeps the first.
metadata = [
_string_value("associated_parts", "ABC"),
_string_value("associated_parts", "XYZ"),
_string_value("env", "prod"),
]
assert metadata_proto_to_dict(metadata) == {
"associated_parts": "ABC",
"env": "prod",
}

def test_empty_metadata(self):
assert metadata_proto_to_dict([]) == {}

def test_round_trip_from_dict(self):
original = {"env": "prod", "build": 1.5, "armed": True}
assert metadata_proto_to_dict(metadata_dict_to_proto(original)) == original
9 changes: 7 additions & 2 deletions python/lib/sift_client/util/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,21 @@ def metadata_dict_to_proto(_metadata: dict[str, str | float | bool]) -> list[Met
def metadata_proto_to_dict(metadata: list[MetadataProto]) -> dict[str, str | float | bool]:
"""Converts a list of MetadataValue objects into a dictionary.

A key may appear multiple times when it holds multiple values
(multi-value metadata). The dictionary keeps the first value of each
key -- the API returns values in canonical order, so this matches the
value every other single-value context (e.g. backend flattening) uses.

Args:
metadata: List of MetadataValue objects.

Returns:
Dictionary of metadata key-value pairs.
Dictionary of metadata key-value pairs (first value per key).
"""
unwrapped_metadata: dict[str, str | float | bool] = {}
for md in metadata:
if md.key.name in unwrapped_metadata:
raise ValueError(f"Key already exists: {md.key.name}")
continue
if md.key.type == MetadataKeyType.METADATA_KEY_TYPE_STRING:
unwrapped_metadata[md.key.name] = md.string_value
elif md.key.type == MetadataKeyType.METADATA_KEY_TYPE_BOOLEAN:
Expand Down
Loading