Skip to content
Open
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
58 changes: 58 additions & 0 deletions tests/test_api_client/test_file_download.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
from pathlib import Path

import pytest

from xero_python.api_client import ApiClient
from xero_python.api_client.configuration import Configuration


class FakeResponse:
def __init__(self, content_disposition, data=b"file contents"):
self.content_disposition = content_disposition
self.data = data

def getheader(self, name):
if name == "Content-Disposition":
return self.content_disposition
return None


@pytest.fixture
def api_client(tmp_path):
configuration = Configuration()
configuration.temp_folder_path = str(tmp_path)
return ApiClient(configuration=configuration)


def deserialize_file(api_client, response):
return Path(api_client._ApiClient__deserialize_file(response))


def test_deserialize_file_keeps_download_within_temp_directory(api_client, tmp_path):
path = deserialize_file(
api_client, FakeResponse('attachment; filename="../outside.txt"')
)

assert path.parent == tmp_path
assert path.name == "outside.txt"
assert path.read_bytes() == b"file contents"
assert not (tmp_path.parent / "outside.txt").exists()


def test_deserialize_file_uses_content_disposition_filename(api_client, tmp_path):
path = deserialize_file(
api_client, FakeResponse('attachment; filename="report.csv"')
)

assert path == tmp_path / "report.csv"
assert path.read_bytes() == b"file contents"


def test_deserialize_file_uses_generated_filename_without_filename_parameter(
api_client, tmp_path
):
path = deserialize_file(api_client, FakeResponse("inline"))

assert path.parent == tmp_path
assert path.read_bytes() == b"file contents"
13 changes: 9 additions & 4 deletions xero_python/api_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,10 +603,15 @@ def __deserialize_file(self, response):

content_disposition = response.getheader("Content-Disposition")
if content_disposition:
filename = re.search(
r'filename=[\'"]?([^\'"\s]+)[\'"]?', content_disposition
).group(1)
path = os.path.join(os.path.dirname(path), filename)
match = re.search(
r'filename=[\'"]?([^\'"\s]+)[\'"]?',
content_disposition,
flags=re.IGNORECASE,
)
if match:
filename = os.path.basename(match.group(1))
if filename not in ("", ".", ".."):
path = os.path.join(os.path.dirname(path), filename)

with open(path, "wb") as f:
f.write(response.data)
Expand Down