Skip to content

Commit

Permalink
Support deviceless recordings (#98)
Browse files Browse the repository at this point in the history
* Support export by key

* Tolerate empty devices imports recordings

* formatting

* lint

* add to upload API for completeness

* add deviceless test

* bump version

---------

Co-authored-by: James Smith <[email protected]>
  • Loading branch information
wkalt and james-rms authored Jan 17, 2024
1 parent a4342bb commit 6670b6b
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 5 deletions.
17 changes: 13 additions & 4 deletions foxglove_data_platform/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,8 @@ def iter_messages(
def download_recording_data(
self,
*,
id: str,
id: Optional[str] = None,
key: Optional[str] = None,
output_format: OutputFormat = OutputFormat.mcap0,
include_attachments: bool = False,
callback: Optional[ProgressCallback] = None,
Expand All @@ -368,13 +369,17 @@ def download_recording_data(
Returns raw data bytes for a recording.
:param id: the ID of the recording.
:param key: the key of the recording.
:param include_attachments: whether to include MCAP attachments in the returned data.
:param output_format: The output format of the data, defaulting to .mcap.
Note: You can only export a .bag file if you originally uploaded a .bag file.
:param callback: an optional callback to report download progress.
"""
if id is None and key is None:
raise RuntimeError("id or key must be provided")
params = {
"recordingId": id,
"key": key,
"includeAttachments": include_attachments,
"outputFormat": output_format.value,
}
Expand Down Expand Up @@ -486,8 +491,8 @@ def get_coverage(

return [
{
"device_id": c["deviceId"],
"device": c["device"],
"device_id": c.get("deviceId"),
"device": c.get("device"),
"start": arrow.get(c["start"]).datetime,
"end": arrow.get(c["end"]).datetime,
}
Expand Down Expand Up @@ -698,7 +703,7 @@ def get_imports(
return [
{
"import_id": i["importId"],
"device_id": i["deviceId"],
"device_id": i.get("deviceId"),
"import_time": arrow.get(i["importTime"]).datetime,
"start": arrow.get(i["start"]).datetime,
"end": arrow.get(i["end"]).datetime,
Expand Down Expand Up @@ -907,6 +912,7 @@ def upload_data(
*,
device_id: Optional[str] = None,
device_name: Optional[str] = None,
key: Optional[str] = None,
filename: str,
data: Union[bytes, IO[Any]],
callback: Optional[SizeProgressCallback] = None,
Expand All @@ -916,6 +922,8 @@ def upload_data(
device_id: Device id of the device from which this data originated.
device_name: Name id of the device from which this data originated.
key: an optional string key to associate with the recording. Any subsequent upload
with the same key will be de-duplicated with this recording.
filename: A filename to associate with the data. The data format will be
inferred from the file extension.
data: The raw data in .bag or .mcap format.
Expand All @@ -925,6 +933,7 @@ def upload_data(
"device.id": device_id,
"device.name": device_name,
"filename": filename,
"key": key,
}
link_response = requests.post(
self.__url__("/v1/data/upload"),
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = foxglove-data-platform
version = 0.13.1
version = 0.14.0
description = Client library for Foxglove Data Platform.
long_description = file: README.md
long_description_content_type = text/markdown
Expand Down
44 changes: 44 additions & 0 deletions tests/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,23 @@ def test_download():
assert data == response_data


@responses.activate
def test_download_recording_data():
download_link = fake.url()
responses.add(
responses.POST,
api_url("/v1/data/stream"),
json={
"link": download_link,
},
)
data = fake.binary(4096)
responses.add(responses.GET, download_link, body=data)
client = Client("test")
response_data = client.download_recording_data(key="test_key")
assert data == response_data


@responses.activate
def test_streaming_upload():
upload_link = fake.url()
Expand Down Expand Up @@ -82,3 +99,30 @@ def test_upload():
device_id=device_id, filename=filename, data=data
)
assert upload_response["link"] == upload_link


@responses.activate
def test_upload_deviceless():
upload_link = fake.url()
key = "abc123"
filename = "test_file.mcap"
responses.add(
responses.POST,
api_url("/v1/data/upload"),
match=[
json_params_matcher(
{
"key": key,
"filename": filename,
},
)
],
json={
"link": upload_link,
},
)
responses.add(responses.PUT, upload_link)
client = Client("test")
data = fake.binary(4096)
upload_response = client.upload_data(data=data, filename=filename, key=key)
assert upload_response["link"] == upload_link

0 comments on commit 6670b6b

Please sign in to comment.