Skip to content

Commit 84c3508

Browse files
authored
[PLT-461] validate attachment value (#1499)
1 parent b726ccf commit 84c3508

File tree

3 files changed

+73
-14
lines changed

3 files changed

+73
-14
lines changed

labelbox/schema/asset_attachment.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,15 @@ def validate_attachment_json(cls, attachment_json: Dict[str, str]) -> None:
4949
raise ValueError(
5050
f"Must provide a `{required_key}` key for each attachment. Found {attachment_json}."
5151
)
52-
cls.validate_attachment_type(attachment_json['type'])
52+
cls.validate_attachment_value(attachment_json['value'])
53+
cls.validate_attachment_type(attachment_json['type'])
54+
55+
@classmethod
56+
def validate_attachment_value(cls, attachment_value: str) -> None:
57+
if not isinstance(attachment_value, str) or attachment_value == "":
58+
raise ValueError(
59+
f"Attachment value must be a non-empty string, got: '{attachment_value}'"
60+
)
5361

5462
@classmethod
5563
def validate_attachment_type(cls, attachment_type: str) -> None:
@@ -72,22 +80,29 @@ def update(self,
7280
type: Optional[str] = None,
7381
value: Optional[str] = None):
7482
"""Updates an attachment on the data row."""
83+
if not name and not type and value is None:
84+
raise ValueError(
85+
"At least one of the following must be provided: name, type, value"
86+
)
87+
88+
query_params = {"attachment_id": self.uid}
7589
if type:
7690
self.validate_attachment_type(type)
91+
query_params["type"] = type
92+
if value is not None:
93+
self.validate_attachment_value(value)
94+
query_params["value"] = value
95+
if name:
96+
query_params["name"] = name
7797

7898
query_str = """mutation updateDataRowAttachmentPyApi($attachment_id: ID!, $name: String, $type: AttachmentType, $value: String) {
7999
updateDataRowAttachment(
80100
where: {id: $attachment_id},
81101
data: {name: $name, type: $type, value: $value}
82102
) { id name type value }
83103
}"""
84-
res = (self.client.execute(
85-
query_str, {
86-
"attachment_id": self.uid,
87-
"name": name,
88-
"type": type,
89-
"value": value
90-
}))['updateDataRowAttachment']
104+
res = (self.client.execute(query_str,
105+
query_params))['updateDataRowAttachment']
91106

92107
self.attachment_name = res['name']
93108
self.attachment_value = res['value']

labelbox/schema/data_row.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,13 @@ def create_attachment(self,
137137
Returns:
138138
`AssetAttachment` DB object.
139139
Raises:
140-
ValueError: asset_type must be one of the supported types.
140+
ValueError: attachment_type must be one of the supported types.
141+
ValueError: attachment_value must be a non-empty string.
141142
"""
142-
Entity.AssetAttachment.validate_attachment_type(attachment_type)
143+
Entity.AssetAttachment.validate_attachment_json({
144+
'type': attachment_type,
145+
'value': attachment_value
146+
})
143147

144148
attachment_type_param = "type"
145149
attachment_value_param = "value"
@@ -220,13 +224,13 @@ def export_v2(
220224
task_name (str): name of remote task
221225
params (CatalogExportParams): export params
222226
223-
227+
224228
>>> dataset = client.get_dataset(DATASET_ID)
225229
>>> task = DataRow.export_v2(
226-
>>> data_rows=[data_row.uid for data_row in dataset.data_rows.list()],
230+
>>> data_rows=[data_row.uid for data_row in dataset.data_rows.list()],
227231
>>> # or a list of DataRow objects: data_rows = data_set.data_rows.list()
228-
>>> # or a list of global_keys=["global_key_1", "global_key_2"],
229-
>>> # Note that exactly one of: data_rows or global_keys parameters can be passed in at a time
232+
>>> # or a list of global_keys=["global_key_1", "global_key_2"],
233+
>>> # Note that exactly one of: data_rows or global_keys parameters can be passed in at a time
230234
>>> # and if data rows ids is present, global keys will be ignored
231235
>>> params={
232236
>>> "performance_details": False,

tests/integration/test_data_rows.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,26 @@ def test_create_data_rows_sync_mixed_upload(dataset, image_url):
747747
assert len(list(dataset.data_rows())) == n_local + n_urls
748748

749749

750+
def test_create_data_row_attachment(data_row):
751+
att = data_row.create_attachment("IMAGE", "https://example.com/image.jpg",
752+
"name")
753+
assert att.attachment_type == "IMAGE"
754+
assert att.attachment_value == "https://example.com/image.jpg"
755+
assert att.attachment_name == "name"
756+
757+
758+
def test_create_data_row_attachment_invalid_type(data_row):
759+
with pytest.raises(ValueError):
760+
data_row.create_attachment("SOME_TYPE", "value", "name")
761+
762+
763+
def test_create_data_row_attachment_invalid_value(data_row):
764+
with pytest.raises(ValueError):
765+
data_row.create_attachment("IMAGE", "", "name")
766+
with pytest.raises(ValueError):
767+
data_row.create_attachment("IMAGE", None, "name")
768+
769+
750770
def test_delete_data_row_attachment(data_row, image_url):
751771
attachments = []
752772

@@ -791,6 +811,26 @@ def test_update_data_row_attachment_invalid_type(data_row):
791811
attachment.update(name="updated name", type="INVALID", value="value")
792812

793813

814+
def test_update_data_row_attachment_invalid_value(data_row):
815+
attachment: AssetAttachment = data_row.create_attachment(
816+
"RAW_TEXT", "value", "name")
817+
assert attachment is not None
818+
with pytest.raises(ValueError):
819+
attachment.update(name="updated name", type="IMAGE", value="")
820+
821+
822+
def test_does_not_update_not_provided_attachment_fields(data_row):
823+
attachment: AssetAttachment = data_row.create_attachment(
824+
"RAW_TEXT", "value", "name")
825+
assert attachment is not None
826+
attachment.update(value=None, name="name")
827+
assert attachment.attachment_value == "value"
828+
attachment.update(name=None, value="value")
829+
assert attachment.attachment_name == "name"
830+
attachment.update(type=None, name="name")
831+
assert attachment.attachment_type == "RAW_TEXT"
832+
833+
794834
def test_create_data_rows_result(client, dataset, image_url):
795835
task = dataset.create_data_rows([
796836
{

0 commit comments

Comments
 (0)