Skip to content

Commit 1ddc46b

Browse files
committed
Merge branch 'release/26.1.0'
2 parents daf8806 + 571786a commit 1ddc46b

16 files changed

Lines changed: 1781 additions & 2 deletions

File tree

CHANGELOG

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
ChangeLog
33
*********
44

5+
26.1.0 (2026-07-09)
6+
===================
7+
8+
- Add oraclecloud limited storage provider, suitable as storage backend for osfstorage.
9+
(thanks, @sh-andriy!)
10+
511
26.0.1 (2026-01-09)
612
===================
713

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "waterbutler"
3-
version = "26.0.1" # TODO: get version from waterbutler/version.py
3+
version = "26.1.0" # TODO: get version from waterbutler/version.py
44
description = "WaterButler Storage Server"
55
authors = ["Center for Open Science <contact@cos.io>"]
66
readme = "README.md"
@@ -91,6 +91,7 @@ googledrive = "waterbutler.providers.googledrive:GoogleDriveProvider"
9191
onedrive = "waterbutler.providers.onedrive:OneDriveProvider"
9292
googlecloud = "waterbutler.providers.googlecloud:GoogleCloudProvider"
9393
azureblobstorage = "waterbutler.providers.azureblobstorage:AzureBlobStorageProvider"
94+
oraclecloud = "waterbutler.providers.oraclecloud:OracleCloudProvider"
9495

9596
[tool.pytest.ini_options]
9697
asyncio_default_fixture_loop_scope = "function"

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
'onedrive = waterbutler.providers.onedrive:OneDriveProvider',
5151
'googlecloud = waterbutler.providers.googlecloud:GoogleCloudProvider',
5252
'azureblobstorage = waterbutler.providers.azureblobstorage:AzureBlobStorageProvider',
53+
'oraclecloud = waterbutler.providers.oraclecloud:OracleCloudProvider',
5354
],
5455
},
5556
)

tests/providers/oraclecloud/__init__.py

Whitespace-only changes.

tests/providers/oraclecloud/fixtures/__init__.py

Whitespace-only changes.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import pytest
2+
3+
4+
@pytest.fixture()
5+
def mock_auth():
6+
return {"name": "Test User", "email": "test@osf.io"}
7+
8+
9+
@pytest.fixture()
10+
def mock_creds():
11+
return {
12+
"json_creds": {
13+
"s3compat": {
14+
"access_key": "fake-access-key-id",
15+
"secret_key": "fake-secret-access-key",
16+
"region": "us-ashburn-1",
17+
},
18+
}
19+
}
20+
21+
22+
@pytest.fixture()
23+
def mock_settings():
24+
return {
25+
"bucket": "test-bucket",
26+
"namespace": "test-namespace",
27+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import pytest
2+
3+
from waterbutler.providers.oraclecloud.metadata import (
4+
BaseOracleCloudMetadata,
5+
OracleCloudFileMetadata,
6+
OracleCloudFolderMetadata,
7+
)
8+
9+
10+
class TestOracleCloudFileMetadata:
11+
12+
def test_file_metadata_from_dict(self):
13+
14+
raw = {
15+
"object_name": "path/to/file.txt",
16+
"content_type": "text/plain",
17+
"last_modified": "Thu, 01 Mar 2025 19:04:45 GMT",
18+
"size": 1024,
19+
"etag": "abc123def456",
20+
"extra": {
21+
"md5": "abc123def456",
22+
},
23+
"time_created": "2025-03-01T19:00:00+00:00",
24+
}
25+
metadata = OracleCloudFileMetadata(raw)
26+
27+
assert isinstance(metadata, BaseOracleCloudMetadata)
28+
assert metadata.provider == "oraclecloud"
29+
assert metadata.kind == "file"
30+
assert metadata.name == "file.txt"
31+
assert metadata.path == "/path/to/file.txt"
32+
assert metadata.content_type == "text/plain"
33+
assert metadata.modified == "Thu, 01 Mar 2025 19:04:45 GMT"
34+
assert metadata.size == 1024
35+
assert metadata.size_as_int == 1024
36+
assert metadata.etag == "abc123def456"
37+
assert metadata.extra["md5"] == "abc123def456"
38+
assert metadata.created_utc == "2025-03-01T19:00:00+00:00"
39+
40+
def test_file_metadata_missing_optional_fields(self):
41+
42+
raw = {
43+
"object_name": "simple.txt",
44+
"content_type": None,
45+
"last_modified": None,
46+
"size": None,
47+
"etag": None,
48+
"extra": {},
49+
}
50+
metadata = OracleCloudFileMetadata(raw)
51+
52+
assert metadata.name == "simple.txt"
53+
assert metadata.content_type is None
54+
assert metadata.modified is None
55+
assert metadata.size is None
56+
assert metadata.etag is None
57+
assert metadata.extra == {}
58+
assert metadata.created_utc is None
59+
60+
def test_file_metadata_from_head_response(self):
61+
62+
headers = {
63+
"Content-Type": "application/pdf",
64+
"Content-Length": "2048",
65+
"Last-Modified": "Fri, 14 Mar 2025 12:00:00 GMT",
66+
"ETag": '"deadbeef"',
67+
}
68+
69+
metadata = OracleCloudFileMetadata.new_from_head_response(
70+
"folder/report.pdf", headers
71+
)
72+
73+
assert metadata.provider == "oraclecloud"
74+
assert metadata.name == "report.pdf"
75+
assert metadata.path == "/folder/report.pdf"
76+
assert metadata.content_type == "application/pdf"
77+
assert metadata.size == 2048
78+
assert metadata.etag == "deadbeef"
79+
assert metadata.extra["md5"] == "deadbeef"
80+
81+
def test_file_metadata_from_s3_list_entry(self):
82+
83+
entry = {
84+
"Key": "data/results.csv",
85+
"LastModified": "2025-03-10T08:00:00.000Z",
86+
"ETag": '"etag123"',
87+
"Size": "4096",
88+
"StorageClass": "STANDARD",
89+
}
90+
91+
metadata = OracleCloudFileMetadata.new_from_s3_list_entry(entry)
92+
93+
assert metadata.name == "results.csv"
94+
assert metadata.path == "/data/results.csv"
95+
assert metadata.size == 4096
96+
assert metadata.etag == "etag123"
97+
assert metadata.extra["md5"] == "etag123"
98+
assert metadata.modified == "2025-03-10T08:00:00.000Z"
99+
assert metadata.created_utc is None
100+
101+
def test_file_metadata_from_s3_list_entry_no_quotes(self):
102+
103+
entry = {
104+
"Key": "file.bin",
105+
"LastModified": "2025-01-01T00:00:00.000Z",
106+
"ETag": "notag",
107+
"Size": "0",
108+
}
109+
110+
metadata = OracleCloudFileMetadata.new_from_s3_list_entry(entry)
111+
112+
assert metadata.name == "file.bin"
113+
assert metadata.etag == "notag"
114+
assert metadata.size == 0
115+
116+
117+
class TestOracleCloudFolderMetadata:
118+
119+
def test_folder_metadata(self):
120+
121+
raw = {"object_name": "path/to/folder/"}
122+
metadata = OracleCloudFolderMetadata(raw)
123+
124+
assert isinstance(metadata, BaseOracleCloudMetadata)
125+
assert metadata.provider == "oraclecloud"
126+
assert metadata.kind == "folder"
127+
assert metadata.name == "folder"
128+
assert metadata.path == "/path/to/folder/"
129+
130+
def test_folder_metadata_nested(self):
131+
132+
raw = {"object_name": "a/b/c/"}
133+
metadata = OracleCloudFolderMetadata(raw)
134+
135+
assert metadata.name == "c"
136+
assert metadata.path == "/a/b/c/"

0 commit comments

Comments
 (0)