dcor-shared 0.7.2__tar.gz → 0.7.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/CHANGELOG +5 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/PKG-INFO +1 -1
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/dcor_shared/__init__.py +1 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/dcor_shared/_version_save.py +1 -1
- dcor_shared-0.7.4/dcor_shared/dcinst.py +20 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/dcor_shared/s3cc.py +75 -3
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/dcor_shared.egg-info/PKG-INFO +1 -1
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/dcor_shared.egg-info/SOURCES.txt +1 -0
- dcor_shared-0.7.4/tests/test_data.py +183 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/tests/test_s3cc.py +51 -10
- dcor_shared-0.7.2/tests/test_data.py +0 -68
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/LICENSE +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/MANIFEST.in +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/README.rst +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/dcor_shared/_version.py +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/dcor_shared/ckan.py +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/dcor_shared/data.py +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/dcor_shared/mime.py +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/dcor_shared/parse.py +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/dcor_shared/paths.py +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/dcor_shared/s3.py +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/dcor_shared/testing.py +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/dcor_shared.egg-info/dependency_links.txt +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/dcor_shared.egg-info/requires.txt +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/dcor_shared.egg-info/top_level.txt +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/setup.cfg +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/setup.py +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/tests/test_basic.py +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/tests/test_s3.py +0 -0
- {dcor_shared-0.7.2 → dcor_shared-0.7.4}/tests/test_testing.py +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# flake8: noqa: F401
|
|
2
2
|
from .ckan import get_ckan_config_option, get_resource_path
|
|
3
3
|
from .data import DUMMY_BYTES, sha256sum, wait_for_resource
|
|
4
|
+
from .dcinst import get_dc_instance
|
|
4
5
|
from .mime import DC_MIME_TYPES, VALID_FORMATS
|
|
5
6
|
from . import paths
|
|
6
7
|
from ._version import version as __version__
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from dclab.rtdc_dataset import RTDC_HDF5
|
|
2
|
+
|
|
3
|
+
from .ckan import get_resource_path
|
|
4
|
+
from . import s3cc
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_dc_instance(rid):
|
|
8
|
+
"""Return an instance of dclab's `RTDCBase` for a resource identifier"""
|
|
9
|
+
# Try local file first
|
|
10
|
+
path = get_resource_path(rid)
|
|
11
|
+
if path.is_file():
|
|
12
|
+
# Disable basins, because they could point to files on the local
|
|
13
|
+
# file system (security).
|
|
14
|
+
return RTDC_HDF5(path, enable_basins=False)
|
|
15
|
+
else:
|
|
16
|
+
# The resource must be on S3
|
|
17
|
+
if s3cc.artifact_exists(rid):
|
|
18
|
+
return s3cc.get_s3_dc_handle(rid)
|
|
19
|
+
else:
|
|
20
|
+
raise ValueError(f"Could not find resource {rid} anywhere")
|
|
@@ -3,16 +3,32 @@
|
|
|
3
3
|
Contains methods to directly interact with CKAN resources that are on S3
|
|
4
4
|
via just the resource ID.
|
|
5
5
|
"""
|
|
6
|
+
from __future__ import annotations
|
|
6
7
|
import functools
|
|
7
|
-
|
|
8
|
+
import pathlib
|
|
8
9
|
from typing import Literal
|
|
10
|
+
import warnings
|
|
9
11
|
|
|
10
12
|
from dclab.rtdc_dataset import fmt_s3
|
|
11
13
|
|
|
12
14
|
from .ckan import get_ckan_config_option
|
|
15
|
+
from .data import sha256sum
|
|
13
16
|
from . import s3
|
|
14
17
|
|
|
15
18
|
|
|
19
|
+
def artifact_exists(
|
|
20
|
+
resource_id: str,
|
|
21
|
+
artifact: Literal["condensed", "preview", "resource"] = "resource"):
|
|
22
|
+
"""Check whether an artifact is available on S3
|
|
23
|
+
|
|
24
|
+
The resource with the identifier `resource_id` must exist in the
|
|
25
|
+
CKAN database.
|
|
26
|
+
"""
|
|
27
|
+
bucket_name, object_name = get_s3_bucket_object_for_artifact(
|
|
28
|
+
resource_id=resource_id, artifact=artifact)
|
|
29
|
+
return s3.object_exists(bucket_name=bucket_name, object_name=object_name)
|
|
30
|
+
|
|
31
|
+
|
|
16
32
|
def compute_checksum(resource_id):
|
|
17
33
|
"""Compute the SHA256 checksum of the corresponding CKAN resource"""
|
|
18
34
|
bucket_name, object_name = get_s3_bucket_object_for_artifact(
|
|
@@ -91,6 +107,9 @@ def get_s3_dc_handle(resource_id):
|
|
|
91
107
|
"dcor_object_store.access_key_id"),
|
|
92
108
|
secret_key=get_ckan_config_option(
|
|
93
109
|
"dcor_object_store.secret_access_key"),
|
|
110
|
+
# Disable basins, because they could point to files on the
|
|
111
|
+
# local file system (security).
|
|
112
|
+
enable_basins=False,
|
|
94
113
|
)
|
|
95
114
|
return ds
|
|
96
115
|
|
|
@@ -130,11 +149,64 @@ def make_resource_public(resource_id: str,
|
|
|
130
149
|
def object_exists(
|
|
131
150
|
resource_id: str,
|
|
132
151
|
artifact: Literal["condensed", "preview", "resource"] = "resource"):
|
|
133
|
-
"""Check whether an
|
|
152
|
+
"""Check whether an artifact is available on S3
|
|
134
153
|
|
|
135
154
|
The resource with the identifier `resource_id` must exist in the
|
|
136
155
|
CKAN database.
|
|
137
156
|
"""
|
|
157
|
+
warnings.warn("`s3cc.object_exists` is deprecated, please use"
|
|
158
|
+
"`s3cc.artifact_exists` instead",
|
|
159
|
+
DeprecationWarning)
|
|
160
|
+
return artifact_exists(resource_id, artifact)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def upload_artifact(
|
|
164
|
+
resource_id: str,
|
|
165
|
+
path_artifact: str | pathlib.Path,
|
|
166
|
+
artifact: Literal["condensed", "preview", "resource"] = "resource",
|
|
167
|
+
sha256: str = None,
|
|
168
|
+
private: bool = None,
|
|
169
|
+
override: bool = False
|
|
170
|
+
):
|
|
171
|
+
"""Upload an artifact to S3
|
|
172
|
+
|
|
173
|
+
Parameters
|
|
174
|
+
----------
|
|
175
|
+
resource_id: str
|
|
176
|
+
The resource identifier for the artifact
|
|
177
|
+
path_artifact: pathlib.Path
|
|
178
|
+
The path to the artifact file
|
|
179
|
+
artifact: str
|
|
180
|
+
The artifact type that the file represents
|
|
181
|
+
sha256: str
|
|
182
|
+
The SHA256 sum of `path_artifcat`, will be computed if not provided
|
|
183
|
+
private: bool
|
|
184
|
+
Whether the dataset that the resource belongs to is private.
|
|
185
|
+
Leave this blank if you don't know and we will do a database
|
|
186
|
+
look-up to determine the correct value.
|
|
187
|
+
override: bool
|
|
188
|
+
Whether to override a possibly existing object on S3.
|
|
189
|
+
"""
|
|
138
190
|
bucket_name, object_name = get_s3_bucket_object_for_artifact(
|
|
139
191
|
resource_id=resource_id, artifact=artifact)
|
|
140
|
-
|
|
192
|
+
|
|
193
|
+
if private is None:
|
|
194
|
+
# User did not say whether the resource is private. We have to
|
|
195
|
+
# find out ourselves.
|
|
196
|
+
import ckan.logic
|
|
197
|
+
res_dict = ckan.logic.get_action('resource_show')(
|
|
198
|
+
context={'ignore_auth': True, 'user': 'default'},
|
|
199
|
+
data_dict={"id": resource_id})
|
|
200
|
+
ds_dict = ckan.logic.get_action('package_show')(
|
|
201
|
+
context={'ignore_auth': True, 'user': 'default'},
|
|
202
|
+
data_dict={'id': res_dict["package_id"]})
|
|
203
|
+
private = ds_dict["private"]
|
|
204
|
+
|
|
205
|
+
rid = resource_id
|
|
206
|
+
s3.upload_file(
|
|
207
|
+
bucket_name=bucket_name,
|
|
208
|
+
object_name=f"{artifact}/{rid[:3]}/{rid[3:6]}/{rid[6:]}",
|
|
209
|
+
path=path_artifact,
|
|
210
|
+
sha256=sha256 or sha256sum(path_artifact),
|
|
211
|
+
private=private,
|
|
212
|
+
override=override)
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import atexit
|
|
2
|
+
import pathlib
|
|
3
|
+
import shutil
|
|
4
|
+
from unittest import mock
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
7
|
+
from ckan import logic
|
|
8
|
+
import ckanext.dcor_schemas.plugin
|
|
9
|
+
|
|
10
|
+
from dcor_shared import (
|
|
11
|
+
get_dc_instance, get_resource_path, s3, sha256sum, wait_for_resource
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
import pytest
|
|
15
|
+
import ckan.tests.factories as factories
|
|
16
|
+
from dcor_shared.testing import make_dataset, synchronous_enqueue_job
|
|
17
|
+
|
|
18
|
+
data_path = pathlib.Path(__file__).parent / "data"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.mark.ckan_config('ckan.plugins', 'dcor_depot dcor_schemas')
|
|
22
|
+
@pytest.mark.usefixtures('clean_db', 'with_request_context')
|
|
23
|
+
@mock.patch('ckan.plugins.toolkit.enqueue_job',
|
|
24
|
+
side_effect=synchronous_enqueue_job)
|
|
25
|
+
def test_get_dc_instance_file(enqueue_job_mock, create_with_upload,
|
|
26
|
+
monkeypatch):
|
|
27
|
+
monkeypatch.setattr(
|
|
28
|
+
ckanext.dcor_schemas.plugin,
|
|
29
|
+
'DISABLE_AFTER_DATASET_CREATE_FOR_CONCURRENT_JOB_TESTS',
|
|
30
|
+
True)
|
|
31
|
+
|
|
32
|
+
user = factories.User()
|
|
33
|
+
owner_org = factories.Organization(users=[{
|
|
34
|
+
'name': user['id'],
|
|
35
|
+
'capacity': 'admin'
|
|
36
|
+
}])
|
|
37
|
+
create_context = {'ignore_auth': False,
|
|
38
|
+
'user': user['name'],
|
|
39
|
+
'api_version': 3}
|
|
40
|
+
ds_dict, _ = make_dataset(
|
|
41
|
+
create_context, owner_org,
|
|
42
|
+
create_with_upload=create_with_upload,
|
|
43
|
+
resource_path=data_path / "calibration_beads_47.rtdc",
|
|
44
|
+
activate=True)
|
|
45
|
+
rid = ds_dict["resources"][0]["id"]
|
|
46
|
+
resource_path = pathlib.Path(get_resource_path(rid))
|
|
47
|
+
assert resource_path.exists(), "sanity check"
|
|
48
|
+
with get_dc_instance(rid) as ds:
|
|
49
|
+
assert str(ds.path) == str(resource_path)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@pytest.mark.ckan_config('ckan.storage_path',
|
|
53
|
+
'/tmp/test_dcor_shared/test_get_dc_instance')
|
|
54
|
+
def test_get_dc_instance_file_fails_without_actual_resource():
|
|
55
|
+
tmp_path = pathlib.Path("/tmp/test_dcor_shared/test_get_dc_instance")
|
|
56
|
+
atexit.register(shutil.rmtree,
|
|
57
|
+
tmp_path,
|
|
58
|
+
ignore_errors=True)
|
|
59
|
+
rid = str(uuid.uuid4())
|
|
60
|
+
resource_path = tmp_path / rid[:3] / rid[3:6] / rid[6:]
|
|
61
|
+
resource_path.parent.mkdir(parents=True)
|
|
62
|
+
shutil.copy2(data_path / "calibration_beads_47.rtdc", resource_path)
|
|
63
|
+
with pytest.raises(logic.NotFound):
|
|
64
|
+
get_dc_instance(rid)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@pytest.mark.ckan_config('ckan.plugins', 'dcor_depot dcor_schemas')
|
|
68
|
+
@pytest.mark.usefixtures('clean_db', 'with_request_context')
|
|
69
|
+
@mock.patch('ckan.plugins.toolkit.enqueue_job',
|
|
70
|
+
side_effect=synchronous_enqueue_job)
|
|
71
|
+
def test_get_dc_instance_s3(enqueue_job_mock, create_with_upload,
|
|
72
|
+
monkeypatch):
|
|
73
|
+
monkeypatch.setattr(
|
|
74
|
+
ckanext.dcor_schemas.plugin,
|
|
75
|
+
'DISABLE_AFTER_DATASET_CREATE_FOR_CONCURRENT_JOB_TESTS',
|
|
76
|
+
True)
|
|
77
|
+
|
|
78
|
+
user = factories.User()
|
|
79
|
+
owner_org = factories.Organization(users=[{
|
|
80
|
+
'name': user['id'],
|
|
81
|
+
'capacity': 'admin'
|
|
82
|
+
}])
|
|
83
|
+
create_context = {'ignore_auth': False,
|
|
84
|
+
'user': user['name'],
|
|
85
|
+
'api_version': 3}
|
|
86
|
+
ds_dict, _ = make_dataset(
|
|
87
|
+
create_context, owner_org,
|
|
88
|
+
create_with_upload=create_with_upload,
|
|
89
|
+
resource_path=data_path / "calibration_beads_47.rtdc",
|
|
90
|
+
activate=True)
|
|
91
|
+
res_dict = ds_dict["resources"][0]
|
|
92
|
+
rid = res_dict["id"]
|
|
93
|
+
resource_path = pathlib.Path(get_resource_path(rid))
|
|
94
|
+
# remove the file, so DCOR falls back to the S3 resource
|
|
95
|
+
resource_path.unlink()
|
|
96
|
+
assert not resource_path.exists(), "sanity check"
|
|
97
|
+
with get_dc_instance(rid) as ds:
|
|
98
|
+
assert ds.path.startswith("http")
|
|
99
|
+
assert res_dict["s3_available"]
|
|
100
|
+
assert res_dict["s3_url"] == ds.path
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@pytest.mark.ckan_config('ckan.plugins', 'dcor_depot dcor_schemas')
|
|
104
|
+
@pytest.mark.usefixtures('clean_db', 'with_request_context')
|
|
105
|
+
@mock.patch('ckan.plugins.toolkit.enqueue_job',
|
|
106
|
+
side_effect=synchronous_enqueue_job)
|
|
107
|
+
def test_get_dc_instance_s3_fails_without_actual_resource(enqueue_job_mock):
|
|
108
|
+
user = factories.User()
|
|
109
|
+
owner_org = factories.Organization(users=[{
|
|
110
|
+
'name': user['id'],
|
|
111
|
+
'capacity': 'admin'
|
|
112
|
+
}])
|
|
113
|
+
rid = str(uuid.uuid4())
|
|
114
|
+
bucket_name = f"circle-{owner_org['id']}"
|
|
115
|
+
object_name = f"resource/{rid[:3]}/{rid[3:6]}/{rid[6:]}"
|
|
116
|
+
upload_path = data_path / "calibration_beads_47.rtdc"
|
|
117
|
+
s3.upload_file(bucket_name=bucket_name,
|
|
118
|
+
object_name=object_name,
|
|
119
|
+
path=upload_path,
|
|
120
|
+
sha256=sha256sum(upload_path)
|
|
121
|
+
)
|
|
122
|
+
with pytest.raises(logic.NotFound):
|
|
123
|
+
get_dc_instance(rid)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_sha256sum(tmp_path):
|
|
127
|
+
p = tmp_path / "test.txt"
|
|
128
|
+
p.write_text("Sum this up!")
|
|
129
|
+
ist = sha256sum(p)
|
|
130
|
+
soll = "d00df55b97a60c78bbb137540e1b60647a5e6b216262a95ab96cafd4519bcf6a"
|
|
131
|
+
assert ist == soll
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def test_wait_for_resource_s3(monkeypatch):
|
|
135
|
+
res_id = str(uuid.uuid4())
|
|
136
|
+
monkeypatch.setattr(logic, "get_action",
|
|
137
|
+
lambda x: lambda context, data_dict: {
|
|
138
|
+
"id": res_id,
|
|
139
|
+
"s3_available": True})
|
|
140
|
+
# Should not raise an exception
|
|
141
|
+
wait_for_resource(res_id)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@pytest.mark.ckan_config('ckan.plugins', '') # disable plugins
|
|
145
|
+
def test_wait_for_resource_no_s3_url_no_plugins(monkeypatch):
|
|
146
|
+
res_id = str(uuid.uuid4())
|
|
147
|
+
monkeypatch.setattr(logic, "get_action",
|
|
148
|
+
lambda x: lambda context, data_dict: {
|
|
149
|
+
"id": res_id})
|
|
150
|
+
# Create the actual path
|
|
151
|
+
rp = pathlib.Path(get_resource_path(res_id))
|
|
152
|
+
rp.parent.mkdir(parents=True, exist_ok=True)
|
|
153
|
+
rp.write_bytes(b"This is not a dummy file, but a fake DC file!")
|
|
154
|
+
# Should not raise an exception
|
|
155
|
+
wait_for_resource(res_id)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@pytest.mark.ckan_config('ckan.plugins', 'dcor_depot')
|
|
159
|
+
def test_wait_for_resource_no_s3_url_with_dcor_depot_plugin(monkeypatch):
|
|
160
|
+
res_id = str(uuid.uuid4())
|
|
161
|
+
monkeypatch.setattr(logic, "get_action",
|
|
162
|
+
lambda x: lambda context, data_dict: {
|
|
163
|
+
"id": res_id})
|
|
164
|
+
# Create the actual path
|
|
165
|
+
rp = pathlib.Path(get_resource_path(res_id))
|
|
166
|
+
rp.parent.mkdir(parents=True, exist_ok=True)
|
|
167
|
+
rp.write_bytes(b"This is not a dummy file, but a fake DC file!")
|
|
168
|
+
rp2 = rp.with_name("test.rtdc")
|
|
169
|
+
rp.rename(rp2)
|
|
170
|
+
rp.symlink_to(rp2)
|
|
171
|
+
|
|
172
|
+
# Should not raise an exception
|
|
173
|
+
wait_for_resource(res_id)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def test_wait_resource_not_available(monkeypatch):
|
|
177
|
+
res_id = str(uuid.uuid4())
|
|
178
|
+
monkeypatch.setattr(logic, "get_action",
|
|
179
|
+
lambda x: lambda context, data_dict: {
|
|
180
|
+
"id": res_id})
|
|
181
|
+
# Should raise an exception
|
|
182
|
+
with pytest.raises(OSError, match="Data import seems to take too long"):
|
|
183
|
+
wait_for_resource(res_id, timeout=1)
|
|
@@ -16,6 +16,23 @@ import requests
|
|
|
16
16
|
data_path = pathlib.Path(__file__).parent / "data"
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
@pytest.mark.ckan_config('ckan.plugins', 'dcor_schemas')
|
|
20
|
+
@pytest.mark.usefixtures('clean_db', 'with_request_context')
|
|
21
|
+
@mock.patch('ckan.plugins.toolkit.enqueue_job',
|
|
22
|
+
side_effect=synchronous_enqueue_job)
|
|
23
|
+
def test_artifact_exists(enqueue_job_mock):
|
|
24
|
+
rid, s3_url, _, org_dict = setup_s3_resource_on_ckan(private=True)
|
|
25
|
+
assert s3cc.artifact_exists(rid)
|
|
26
|
+
# Delete the object
|
|
27
|
+
s3_client, _, _ = s3.get_s3()
|
|
28
|
+
bucket_name, object_name = s3cc.get_s3_bucket_object_for_artifact(rid)
|
|
29
|
+
s3_client.delete_object(
|
|
30
|
+
Bucket=bucket_name,
|
|
31
|
+
Key=object_name
|
|
32
|
+
)
|
|
33
|
+
assert not s3cc.artifact_exists(rid)
|
|
34
|
+
|
|
35
|
+
|
|
19
36
|
def setup_s3_resource_on_ckan(private=False):
|
|
20
37
|
"""Create an S3 resource in CKAN"""
|
|
21
38
|
user = factories.User()
|
|
@@ -151,14 +168,38 @@ def test_make_resource_public(enqueue_job_mock):
|
|
|
151
168
|
@pytest.mark.usefixtures('clean_db', 'with_request_context')
|
|
152
169
|
@mock.patch('ckan.plugins.toolkit.enqueue_job',
|
|
153
170
|
side_effect=synchronous_enqueue_job)
|
|
154
|
-
def
|
|
171
|
+
def test_upload_artifact(enqueue_job_mock, tmp_path):
|
|
155
172
|
rid, s3_url, _, org_dict = setup_s3_resource_on_ckan(private=True)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
)
|
|
164
|
-
|
|
173
|
+
path_fake_preview = tmp_path / "test_preview.jpg"
|
|
174
|
+
path_fake_preview.write_text("This is not a real image!")
|
|
175
|
+
# upload the preview
|
|
176
|
+
s3cc.upload_artifact(rid,
|
|
177
|
+
path_artifact=path_fake_preview,
|
|
178
|
+
artifact="preview")
|
|
179
|
+
# make sure that worked
|
|
180
|
+
assert s3cc.artifact_exists(rid, "preview")
|
|
181
|
+
# attempt to download the private artifact
|
|
182
|
+
resp1 = requests.get(s3_url.replace("resource", "preview"))
|
|
183
|
+
assert not resp1.ok, "sanity check"
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@pytest.mark.ckan_config('ckan.plugins', 'dcor_schemas')
|
|
187
|
+
@pytest.mark.usefixtures('clean_db', 'with_request_context')
|
|
188
|
+
@mock.patch('ckan.plugins.toolkit.enqueue_job',
|
|
189
|
+
side_effect=synchronous_enqueue_job)
|
|
190
|
+
def test_upload_artifact_public(enqueue_job_mock, tmp_path):
|
|
191
|
+
rid, s3_url, _, org_dict = setup_s3_resource_on_ckan(private=True)
|
|
192
|
+
path_fake_preview = tmp_path / "test_preview.jpg"
|
|
193
|
+
path_fake_preview.write_text("This is not a real image!")
|
|
194
|
+
# upload the preview
|
|
195
|
+
s3cc.upload_artifact(rid,
|
|
196
|
+
path_artifact=path_fake_preview,
|
|
197
|
+
artifact="preview",
|
|
198
|
+
# force public resource even though dataset is not
|
|
199
|
+
# (this has no real-life use case)
|
|
200
|
+
private=False)
|
|
201
|
+
# make sure that worked
|
|
202
|
+
assert s3cc.artifact_exists(rid, "preview")
|
|
203
|
+
# attempt to download the private artifact
|
|
204
|
+
resp1 = requests.get(s3_url.replace("resource", "preview"))
|
|
205
|
+
assert resp1.ok, "preview should be public"
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import pathlib
|
|
2
|
-
import uuid
|
|
3
|
-
|
|
4
|
-
from ckan import logic
|
|
5
|
-
|
|
6
|
-
import pytest
|
|
7
|
-
|
|
8
|
-
from dcor_shared import get_resource_path, sha256sum, wait_for_resource
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def test_sha256sum(tmp_path):
|
|
12
|
-
p = tmp_path / "test.txt"
|
|
13
|
-
p.write_text("Sum this up!")
|
|
14
|
-
ist = sha256sum(p)
|
|
15
|
-
soll = "d00df55b97a60c78bbb137540e1b60647a5e6b216262a95ab96cafd4519bcf6a"
|
|
16
|
-
assert ist == soll
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def test_wait_for_resource_s3(monkeypatch):
|
|
20
|
-
res_id = str(uuid.uuid4())
|
|
21
|
-
monkeypatch.setattr(logic, "get_action",
|
|
22
|
-
lambda x: lambda context, data_dict: {
|
|
23
|
-
"id": res_id,
|
|
24
|
-
"s3_available": True})
|
|
25
|
-
# Should not raise an exception
|
|
26
|
-
wait_for_resource(res_id)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@pytest.mark.ckan_config('ckan.plugins', '') # disable plugins
|
|
30
|
-
def test_wait_for_resource_no_s3_url_no_plugins(monkeypatch):
|
|
31
|
-
res_id = str(uuid.uuid4())
|
|
32
|
-
monkeypatch.setattr(logic, "get_action",
|
|
33
|
-
lambda x: lambda context, data_dict: {
|
|
34
|
-
"id": res_id})
|
|
35
|
-
# Create the actual path
|
|
36
|
-
rp = pathlib.Path(get_resource_path(res_id))
|
|
37
|
-
rp.parent.mkdir(parents=True, exist_ok=True)
|
|
38
|
-
rp.write_bytes(b"This is not a dummy file, but a fake DC file!")
|
|
39
|
-
# Should not raise an exception
|
|
40
|
-
wait_for_resource(res_id)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@pytest.mark.ckan_config('ckan.plugins', 'dcor_depot')
|
|
44
|
-
def test_wait_for_resource_no_s3_url_with_dcor_depot_plugin(monkeypatch):
|
|
45
|
-
res_id = str(uuid.uuid4())
|
|
46
|
-
monkeypatch.setattr(logic, "get_action",
|
|
47
|
-
lambda x: lambda context, data_dict: {
|
|
48
|
-
"id": res_id})
|
|
49
|
-
# Create the actual path
|
|
50
|
-
rp = pathlib.Path(get_resource_path(res_id))
|
|
51
|
-
rp.parent.mkdir(parents=True, exist_ok=True)
|
|
52
|
-
rp.write_bytes(b"This is not a dummy file, but a fake DC file!")
|
|
53
|
-
rp2 = rp.with_name("test.rtdc")
|
|
54
|
-
rp.rename(rp2)
|
|
55
|
-
rp.symlink_to(rp2)
|
|
56
|
-
|
|
57
|
-
# Should not raise an exception
|
|
58
|
-
wait_for_resource(res_id)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def test_wait_resource_not_available(monkeypatch):
|
|
62
|
-
res_id = str(uuid.uuid4())
|
|
63
|
-
monkeypatch.setattr(logic, "get_action",
|
|
64
|
-
lambda x: lambda context, data_dict: {
|
|
65
|
-
"id": res_id})
|
|
66
|
-
# Should raise an exception
|
|
67
|
-
with pytest.raises(OSError, match="Data import seems to take too long"):
|
|
68
|
-
wait_for_resource(res_id, timeout=1)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|