hexkit 8.1.0__tar.gz → 8.2.0__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.
- {hexkit-8.1.0/src/hexkit.egg-info → hexkit-8.2.0}/PKG-INFO +11 -3
- {hexkit-8.1.0 → hexkit-8.2.0}/pyproject.toml +31 -5
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/config.py +5 -6
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/protocols/objstorage.py +57 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/provider/daosub.py +2 -2
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/provider/eventsub.py +4 -2
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/testutils.py +3 -3
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/migrations/helpers.py +4 -4
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/provider/dao.py +1 -1
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/provider/kvstore.py +6 -2
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/provider/utils.py +1 -1
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongokafka/__init__.py +2 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongokafka/provider/daopub.py +9 -2
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/redis/provider/kvstore.py +8 -3
- hexkit-8.2.0/src/hexkit/providers/s3/__init__.py +36 -0
- hexkit-8.2.0/src/hexkit/providers/s3/config.py +68 -0
- {hexkit-8.1.0/src/hexkit/providers/s3 → hexkit-8.2.0/src/hexkit/providers/s3/provider}/__init__.py +16 -4
- hexkit-8.2.0/src/hexkit/providers/s3/provider/kvstore.py +250 -0
- hexkit-8.1.0/src/hexkit/providers/s3/provider.py → hexkit-8.2.0/src/hexkit/providers/s3/provider/objstorage.py +86 -111
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/s3/testutils/_fixtures.py +1 -1
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/testing/__init__.py +10 -0
- hexkit-8.2.0/src/hexkit/providers/testing/kvstore.py +222 -0
- hexkit-8.1.0/src/hexkit/providers/testing/s3.py → hexkit-8.2.0/src/hexkit/providers/testing/objstorage.py +43 -16
- hexkit-8.2.0/src/hexkit/providers/vault/__init__.py +33 -0
- hexkit-8.2.0/src/hexkit/providers/vault/config.py +118 -0
- hexkit-8.2.0/src/hexkit/providers/vault/provider/__init__.py +20 -0
- hexkit-8.2.0/src/hexkit/providers/vault/provider/client.py +131 -0
- hexkit-8.2.0/src/hexkit/providers/vault/provider/kvstore.py +275 -0
- hexkit-8.2.0/src/hexkit/providers/vault/testutils.py +258 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/utils.py +3 -2
- {hexkit-8.1.0 → hexkit-8.2.0/src/hexkit.egg-info}/PKG-INFO +11 -3
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit.egg-info/SOURCES.txt +12 -2
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit.egg-info/requires.txt +9 -2
- {hexkit-8.1.0 → hexkit-8.2.0}/LICENSE +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/MANIFEST.in +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/README.md +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/setup.cfg +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/__init__.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/__main__.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/base.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/correlation.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/custom_types.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/log.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/opentelemetry.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/protocols/__init__.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/protocols/dao.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/protocols/daopub.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/protocols/daosub.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/protocols/eventpub.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/protocols/eventsub.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/protocols/kvstore.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/__init__.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/__init__.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/config.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/provider/__init__.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/provider/eventpub.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/provider/utils.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/akafka/testcontainer.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/__init__.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/config.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/migrations/__init__.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/migrations/_manager.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/migrations/_utils.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/provider/__init__.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/provider/client.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongodb/testutils.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongokafka/config.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongokafka/provider/__init__.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongokafka/provider/persistent_pub.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/mongokafka/testutils.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/redis/__init__.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/redis/config.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/redis/provider/__init__.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/redis/provider/client.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/redis/testutils.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/s3/test_files/__init__.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/s3/test_files/test_file1.yaml +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/s3/test_files/test_file2.yaml +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/s3/test_files/test_file3.yaml +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/s3/test_files/test_file4.yaml +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/s3/testutils/__init__.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/s3/testutils/_typical_workflow.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/s3/testutils/_utils.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/testing/dao.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/providers/testing/eventpub.py +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit/py.typed +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit.egg-info/dependency_links.txt +0 -0
- {hexkit-8.1.0 → hexkit-8.2.0}/src/hexkit.egg-info/top_level.txt +0 -0
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hexkit
|
|
3
|
-
Version: 8.
|
|
3
|
+
Version: 8.2.0
|
|
4
4
|
Summary: A Toolkit for Building Microservices using the Hexagonal Architecture
|
|
5
5
|
Author-email: "German Human Genome Phenome Archive (GHGA)" <contact@ghga.de>
|
|
6
6
|
License: Apache 2.0
|
|
7
7
|
Project-URL: Repository, https://github.com/ghga-de/hexkit
|
|
8
|
+
Project-URL: Documentation, https://ghga-de.github.io/hexkit/
|
|
9
|
+
Project-URL: Release Notes, https://github.com/ghga-de/hexkit/releases
|
|
10
|
+
Project-URL: Issue Tracker, https://github.com/ghga-de/hexkit/issues
|
|
8
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
9
12
|
Classifier: Operating System :: POSIX :: Linux
|
|
10
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -23,7 +26,7 @@ Requires-Dist: pydantic<3,>=2.12
|
|
|
23
26
|
Requires-Dist: pydantic_settings<3,>=2.12
|
|
24
27
|
Requires-Dist: PyYAML<7,>=6
|
|
25
28
|
Provides-Extra: akafka
|
|
26
|
-
Requires-Dist: aiokafka[lz4,snappy,zstd]~=0.
|
|
29
|
+
Requires-Dist: aiokafka[lz4,snappy,zstd]~=0.14.0; extra == "akafka"
|
|
27
30
|
Requires-Dist: jsonschema<5,>=4.25; extra == "akafka"
|
|
28
31
|
Provides-Extra: s3
|
|
29
32
|
Requires-Dist: boto3<2,>=1.42; extra == "s3"
|
|
@@ -32,6 +35,8 @@ Provides-Extra: mongodb
|
|
|
32
35
|
Requires-Dist: pymongo<5,>=4.16; extra == "mongodb"
|
|
33
36
|
Provides-Extra: redis
|
|
34
37
|
Requires-Dist: redis<8,>=7.1; extra == "redis"
|
|
38
|
+
Provides-Extra: vault
|
|
39
|
+
Requires-Dist: hvac<3,>=2.4; extra == "vault"
|
|
35
40
|
Provides-Extra: opentelemetry-base
|
|
36
41
|
Requires-Dist: opentelemetry-sdk<2,>=1.39.1; extra == "opentelemetry-base"
|
|
37
42
|
Requires-Dist: opentelemetry-exporter-otlp<2,>=1.39.1; extra == "opentelemetry-base"
|
|
@@ -74,8 +79,11 @@ Requires-Dist: testcontainers[mongo]<5,>=4.14; extra == "test-mongodb"
|
|
|
74
79
|
Provides-Extra: test-redis
|
|
75
80
|
Requires-Dist: hexkit[redis]; extra == "test-redis"
|
|
76
81
|
Requires-Dist: testcontainers[redis]<5,>=4.14; extra == "test-redis"
|
|
82
|
+
Provides-Extra: test-vault
|
|
83
|
+
Requires-Dist: hexkit[vault]; extra == "test-vault"
|
|
84
|
+
Requires-Dist: testcontainers<5,>=4.14; extra == "test-vault"
|
|
77
85
|
Provides-Extra: test
|
|
78
|
-
Requires-Dist: hexkit[test-akafka,test-mongodb,test-s3]; extra == "test"
|
|
86
|
+
Requires-Dist: hexkit[test-akafka,test-mongodb,test-s3,test-vault]; extra == "test"
|
|
79
87
|
Provides-Extra: all
|
|
80
88
|
Requires-Dist: hexkit[test]; extra == "all"
|
|
81
89
|
Requires-Dist: hexkit[opentelemetry]; extra == "all"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[build-system]
|
|
2
2
|
requires = [
|
|
3
|
-
"setuptools>=
|
|
3
|
+
"setuptools>=82",
|
|
4
4
|
]
|
|
5
5
|
build-backend = "setuptools.build_meta"
|
|
6
6
|
|
|
@@ -23,7 +23,7 @@ classifiers = [
|
|
|
23
23
|
"Intended Audience :: Developers",
|
|
24
24
|
]
|
|
25
25
|
name = "hexkit"
|
|
26
|
-
version = "8.
|
|
26
|
+
version = "8.2.0"
|
|
27
27
|
description = "A Toolkit for Building Microservices using the Hexagonal Architecture"
|
|
28
28
|
dependencies = [
|
|
29
29
|
"opentelemetry-api >=1.39, <2",
|
|
@@ -37,7 +37,7 @@ text = "Apache 2.0"
|
|
|
37
37
|
|
|
38
38
|
[project.optional-dependencies]
|
|
39
39
|
akafka = [
|
|
40
|
-
"aiokafka[zstd,lz4,snappy]~=0.
|
|
40
|
+
"aiokafka[zstd,lz4,snappy]~=0.14.0",
|
|
41
41
|
"jsonschema >=4.25, <5",
|
|
42
42
|
]
|
|
43
43
|
s3 = [
|
|
@@ -50,6 +50,9 @@ mongodb = [
|
|
|
50
50
|
redis = [
|
|
51
51
|
"redis >=7.1, <8",
|
|
52
52
|
]
|
|
53
|
+
vault = [
|
|
54
|
+
"hvac >=2.4, <3",
|
|
55
|
+
]
|
|
53
56
|
opentelemetry-base = [
|
|
54
57
|
"opentelemetry-sdk >=1.39.1, <2",
|
|
55
58
|
"opentelemetry-exporter-otlp >=1.39.1, <2",
|
|
@@ -103,8 +106,12 @@ test-redis = [
|
|
|
103
106
|
"hexkit[redis]",
|
|
104
107
|
"testcontainers[redis] >=4.14, <5",
|
|
105
108
|
]
|
|
109
|
+
test-vault = [
|
|
110
|
+
"hexkit[vault]",
|
|
111
|
+
"testcontainers >=4.14, <5",
|
|
112
|
+
]
|
|
106
113
|
test = [
|
|
107
|
-
"hexkit[test-akafka,test-s3,test-mongodb]",
|
|
114
|
+
"hexkit[test-akafka,test-s3,test-mongodb,test-vault]",
|
|
108
115
|
]
|
|
109
116
|
all = [
|
|
110
117
|
"hexkit[test]",
|
|
@@ -113,6 +120,9 @@ all = [
|
|
|
113
120
|
|
|
114
121
|
[project.urls]
|
|
115
122
|
Repository = "https://github.com/ghga-de/hexkit"
|
|
123
|
+
Documentation = "https://ghga-de.github.io/hexkit/"
|
|
124
|
+
"Release Notes" = "https://github.com/ghga-de/hexkit/releases"
|
|
125
|
+
"Issue Tracker" = "https://github.com/ghga-de/hexkit/issues"
|
|
116
126
|
|
|
117
127
|
[tool.setuptools.packages.find]
|
|
118
128
|
where = [
|
|
@@ -235,7 +245,7 @@ source = [
|
|
|
235
245
|
[tool.tox]
|
|
236
246
|
legacy_tox_ini = """
|
|
237
247
|
[tox]
|
|
238
|
-
env_list = py3{10,11,12,13}
|
|
248
|
+
env_list = py3{10,11,12,13}, docs
|
|
239
249
|
|
|
240
250
|
[gh-actions]
|
|
241
251
|
python =
|
|
@@ -244,6 +254,22 @@ legacy_tox_ini = """
|
|
|
244
254
|
3.12: py312
|
|
245
255
|
3.13: py313
|
|
246
256
|
|
|
257
|
+
[testenv:docs]
|
|
258
|
+
basepython = python3.13
|
|
259
|
+
allowlist_externals =
|
|
260
|
+
mv
|
|
261
|
+
rm
|
|
262
|
+
deps =
|
|
263
|
+
--no-deps -r ./lock/requirements-dev.txt
|
|
264
|
+
commands =
|
|
265
|
+
rm -rf site/api
|
|
266
|
+
pdoc -d google -o docs/api hexkit
|
|
267
|
+
mv docs/api/index.html docs/api/index.md
|
|
268
|
+
mkdocs build
|
|
269
|
+
mv docs/api/index.md docs/api/index.html
|
|
270
|
+
rm -rf site/api
|
|
271
|
+
mv docs/api site/api
|
|
272
|
+
|
|
247
273
|
[testenv]
|
|
248
274
|
pass_env =
|
|
249
275
|
TC_HOST
|
|
@@ -112,18 +112,16 @@ def config_from_yaml(
|
|
|
112
112
|
"""
|
|
113
113
|
|
|
114
114
|
def decorator(settings) -> Callable:
|
|
115
|
-
# settings should be a pydantic BaseSetting,
|
|
116
|
-
# there is no type hint here to not restrict
|
|
117
|
-
# autocompletion for attributes of the
|
|
118
|
-
# modified settings class returned by the
|
|
119
|
-
# contructor_wrapper
|
|
120
115
|
"""The actual decorator function.
|
|
121
116
|
|
|
122
117
|
Args
|
|
123
118
|
settings (BaseSettings):
|
|
124
119
|
A pydantic BaseSettings class to be modified.
|
|
125
120
|
"""
|
|
126
|
-
#
|
|
121
|
+
# The settings argument should be a pydantic BaseSetting,
|
|
122
|
+
# there is no type hint here to not restrict autocompletion for attributes
|
|
123
|
+
# of the modified settings class returned by the constructor_wrapper.
|
|
124
|
+
# Check if settings inherits from pydantic BaseSettings:
|
|
127
125
|
if not issubclass(settings, BaseSettings):
|
|
128
126
|
raise TypeError(
|
|
129
127
|
"The specified settings class is not a subclass of pydantic_settings.BaseSettings"
|
|
@@ -152,6 +150,7 @@ def config_from_yaml(
|
|
|
152
150
|
frozen=True,
|
|
153
151
|
env_prefix=f"{prefix}_",
|
|
154
152
|
env_file=(".env", dotenv_prefix / ".env"),
|
|
153
|
+
env_nested_delimiter="__",
|
|
155
154
|
)
|
|
156
155
|
|
|
157
156
|
@classmethod
|
|
@@ -195,6 +195,38 @@ class ObjectStorageProtocol(ABC):
|
|
|
195
195
|
bucket_id=bucket_id, object_id=object_id
|
|
196
196
|
)
|
|
197
197
|
|
|
198
|
+
async def list_parts(
|
|
199
|
+
self,
|
|
200
|
+
*,
|
|
201
|
+
bucket_id: str,
|
|
202
|
+
object_id: str,
|
|
203
|
+
upload_id: str,
|
|
204
|
+
max_parts: int | None = None,
|
|
205
|
+
first_part_no: int | None = None,
|
|
206
|
+
) -> list[dict]:
|
|
207
|
+
"""Lists the parts that have been uploaded for a specific multipart upload.
|
|
208
|
+
|
|
209
|
+
Specify `max_parts` to return a limited parts list. If not specified, all
|
|
210
|
+
parts will be returned (up to 10,000).
|
|
211
|
+
|
|
212
|
+
Specify `first_part_no` to get parts starting with that part number. If not
|
|
213
|
+
specified, retrieved parts will start with the first part. Part numbers
|
|
214
|
+
start at 1, not 0.
|
|
215
|
+
|
|
216
|
+
Raises:
|
|
217
|
+
ValueError if `max_parts` or `first_part_no` are invalid.
|
|
218
|
+
MultiPartUploadNotFoundError if no upload with `upload_id` exists.
|
|
219
|
+
"""
|
|
220
|
+
self._validate_bucket_id(bucket_id)
|
|
221
|
+
self._validate_object_id(object_id)
|
|
222
|
+
return await self._list_parts(
|
|
223
|
+
bucket_id=bucket_id,
|
|
224
|
+
object_id=object_id,
|
|
225
|
+
upload_id=upload_id,
|
|
226
|
+
max_parts=max_parts,
|
|
227
|
+
first_part_no=first_part_no,
|
|
228
|
+
)
|
|
229
|
+
|
|
198
230
|
async def get_all_multipart_uploads(self, *, bucket_id: str) -> dict[str, str]:
|
|
199
231
|
"""Gets all active multipart uploads for the given bucket ID.
|
|
200
232
|
|
|
@@ -440,6 +472,31 @@ class ObjectStorageProtocol(ABC):
|
|
|
440
472
|
"""
|
|
441
473
|
...
|
|
442
474
|
|
|
475
|
+
@abstractmethod
|
|
476
|
+
async def _list_parts(
|
|
477
|
+
self,
|
|
478
|
+
*,
|
|
479
|
+
bucket_id: str,
|
|
480
|
+
object_id: str,
|
|
481
|
+
upload_id: str,
|
|
482
|
+
max_parts: int | None = None,
|
|
483
|
+
first_part_no: int | None = None,
|
|
484
|
+
) -> list[dict]:
|
|
485
|
+
"""Lists the parts that have been uploaded for a specific multipart upload.
|
|
486
|
+
|
|
487
|
+
Specify `max_parts` to return a limited parts list. If not specified, all
|
|
488
|
+
parts will be returned (up to 10,000).
|
|
489
|
+
|
|
490
|
+
Specify `first_part_no` to get parts starting with that part number. If not
|
|
491
|
+
specified, retrieved parts will start with the first part. Part numbers
|
|
492
|
+
start at 1, not 0.
|
|
493
|
+
|
|
494
|
+
Raises:
|
|
495
|
+
ValueError if `max_parts` or `first_part_no` are invalid.
|
|
496
|
+
MultiPartUploadNotFoundError if no upload with `upload_id` exists.
|
|
497
|
+
"""
|
|
498
|
+
...
|
|
499
|
+
|
|
443
500
|
@abstractmethod
|
|
444
501
|
async def _get_all_multipart_uploads(self, *, bucket_id: str) -> dict[str, str]:
|
|
445
502
|
"""Gets all active multipart uploads for the given bucket ID.
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
import logging
|
|
22
|
-
from collections.abc import Sequence
|
|
22
|
+
from collections.abc import AsyncGenerator, Sequence
|
|
23
23
|
from contextlib import asynccontextmanager
|
|
24
24
|
|
|
25
25
|
from aiokafka import AIOKafkaConsumer
|
|
@@ -57,7 +57,7 @@ class KafkaOutboxSubscriber(InboundProviderBase):
|
|
|
57
57
|
translators: Sequence[DaoSubscriberProtocol],
|
|
58
58
|
dlq_publisher: EventPublisherProtocol | None = None,
|
|
59
59
|
kafka_consumer_cls: type[KafkaConsumerCompatible] = AIOKafkaConsumer,
|
|
60
|
-
):
|
|
60
|
+
) -> AsyncGenerator["KafkaOutboxSubscriber", None]:
|
|
61
61
|
"""Setup and teardown an instance of the provider.
|
|
62
62
|
|
|
63
63
|
Args:
|
|
@@ -331,9 +331,11 @@ class KafkaConsumerCompatible(Protocol):
|
|
|
331
331
|
An identifier for the consumer group.
|
|
332
332
|
auto_offset_reset:
|
|
333
333
|
Can be set to "earliest".
|
|
334
|
-
|
|
334
|
+
enable_auto_commit:
|
|
335
|
+
Whether to enable auto-committing of offsets.
|
|
336
|
+
key_deserializer:
|
|
335
337
|
Function to deserialize the keys into strings.
|
|
336
|
-
|
|
338
|
+
value_deserializer:
|
|
337
339
|
Function to deserialize the values into strings.
|
|
338
340
|
max_partition_fetch_bytes:
|
|
339
341
|
Maximum receivable message size
|
|
@@ -644,11 +644,11 @@ class KafkaContainerFixture(KafkaContainer):
|
|
|
644
644
|
"""Run the given command in the kafka testcontainer.
|
|
645
645
|
|
|
646
646
|
Args:
|
|
647
|
-
|
|
648
|
-
|
|
647
|
+
command: The full command to run.
|
|
648
|
+
run_in_shell: If True, will run the command in a shell.
|
|
649
649
|
|
|
650
650
|
Raises:
|
|
651
|
-
|
|
651
|
+
RuntimeError: when the exit code returned by the command is not zero.
|
|
652
652
|
"""
|
|
653
653
|
cmd = ["sh", "-c", command] if run_in_shell else command
|
|
654
654
|
exit_code, output = self.get_wrapped_container().exec_run(cmd)
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
# See the License for the specific language governing permissions and
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
|
|
16
|
-
"""Prefab functions to help with migrations caused by changes in
|
|
16
|
+
"""Prefab functions to help with migrations caused by changes in hexkit."""
|
|
17
17
|
|
|
18
18
|
from collections.abc import Awaitable, Callable
|
|
19
19
|
from datetime import datetime, timezone
|
|
@@ -34,7 +34,7 @@ def convert_uuids_and_datetimes_v6(
|
|
|
34
34
|
uuid_fields: list[str] | None = None,
|
|
35
35
|
date_fields: list[str] | None = None,
|
|
36
36
|
) -> Callable[[Document], Awaitable[Document]]:
|
|
37
|
-
"""Produce a function to convert a document to the format expected by
|
|
37
|
+
"""Produce a function to convert a document to the format expected by hexkit v6.
|
|
38
38
|
|
|
39
39
|
If `uuid_fields` is provided, it will convert those fields from string to UUID.
|
|
40
40
|
If `date_fields` is provided, it will convert those fields from isoformat strings
|
|
@@ -60,7 +60,7 @@ def convert_uuids_and_datetimes_v6(
|
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
async def convert_outbox_correlation_id_v6(doc: Document) -> Document:
|
|
63
|
-
"""Convert an outbox document's correlation ID to the format expected by
|
|
63
|
+
"""Convert an outbox document's correlation ID to the format expected by hexkit v6.
|
|
64
64
|
|
|
65
65
|
This will only convert the __metadata__.correlation_id to a UUID.
|
|
66
66
|
|
|
@@ -77,7 +77,7 @@ _convert_persistent_event_existing_fields_v6 = convert_uuids_and_datetimes_v6(
|
|
|
77
77
|
|
|
78
78
|
|
|
79
79
|
async def convert_persistent_event_v6(doc: Document) -> Document:
|
|
80
|
-
"""Convert a persistent event document to the format expected by
|
|
80
|
+
"""Convert a persistent event document to the format expected by hexkit v6.
|
|
81
81
|
|
|
82
82
|
This will:
|
|
83
83
|
- convert correlation_id to UUID
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
"""MongoDB-based provider implementing the DaoFactoryProtocol.
|
|
18
18
|
|
|
19
|
-
Utilities for testing are located in
|
|
19
|
+
Utilities for testing are located in `../testutils.py`.
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
22
|
from collections.abc import AsyncIterator, Callable, Collection, Mapping
|
|
@@ -83,9 +83,13 @@ class MongoDbBaseKeyValueStore(ABC, KeyValueStoreProtocol):
|
|
|
83
83
|
client: An instance of an async MongoDB client.
|
|
84
84
|
config: MongoDB-specific config parameters.
|
|
85
85
|
collection_name: Name of the collection to hold the key-value pairs.
|
|
86
|
-
|
|
86
|
+
|
|
87
|
+
You might be able to pass additional arguments specific to subclasses.
|
|
87
88
|
"""
|
|
88
|
-
|
|
89
|
+
config_args = ", ".join(
|
|
90
|
+
f"{k}={v!r}" for k, v in config.model_dump(exclude_defaults=True).items()
|
|
91
|
+
)
|
|
92
|
+
args = [f"config=MongoDbConfig({config_args})"]
|
|
89
93
|
if collection_name != DEFAULT_KV_COLLECTION_NAME:
|
|
90
94
|
args.append(f"collection_name={collection_name!r}")
|
|
91
95
|
for key, val in kwargs.items():
|
|
@@ -21,6 +21,7 @@ from .provider import (
|
|
|
21
21
|
CHANGE_EVENT_TYPE,
|
|
22
22
|
DELETE_EVENT_TYPE,
|
|
23
23
|
MongoKafkaDaoPublisherFactory,
|
|
24
|
+
PersistentKafkaEvent,
|
|
24
25
|
PersistentKafkaPublisher,
|
|
25
26
|
document_to_dto,
|
|
26
27
|
dto_to_document,
|
|
@@ -31,6 +32,7 @@ __all__ = [
|
|
|
31
32
|
"DELETE_EVENT_TYPE",
|
|
32
33
|
"MongoKafkaConfig",
|
|
33
34
|
"MongoKafkaDaoPublisherFactory",
|
|
35
|
+
"PersistentKafkaEvent",
|
|
34
36
|
"PersistentKafkaPublisher",
|
|
35
37
|
"document_to_dto",
|
|
36
38
|
"dto_to_document",
|
|
@@ -19,7 +19,14 @@
|
|
|
19
19
|
Requires dependencies of the `akafka` and `mongodb` extras.
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
|
-
from collections.abc import
|
|
22
|
+
from collections.abc import (
|
|
23
|
+
AsyncGenerator,
|
|
24
|
+
AsyncIterator,
|
|
25
|
+
Awaitable,
|
|
26
|
+
Callable,
|
|
27
|
+
Collection,
|
|
28
|
+
Mapping,
|
|
29
|
+
)
|
|
23
30
|
from contextlib import AbstractAsyncContextManager, asynccontextmanager, contextmanager
|
|
24
31
|
from functools import partial
|
|
25
32
|
from typing import Any, Generic
|
|
@@ -476,7 +483,7 @@ class MongoKafkaDaoPublisherFactory(DaoPublisherFactoryProtocol[MongoDbIndex]):
|
|
|
476
483
|
*,
|
|
477
484
|
config: MongoKafkaConfig,
|
|
478
485
|
kafka_producer_cls: type[KafkaProducerCompatible] = AIOKafkaProducer,
|
|
479
|
-
):
|
|
486
|
+
) -> AsyncGenerator["MongoKafkaDaoPublisherFactory", None]:
|
|
480
487
|
"""Setup and teardown an instance of the provider.
|
|
481
488
|
This method will automatically close the DB connection upon exit.
|
|
482
489
|
|
|
@@ -68,7 +68,8 @@ class RedisBaseKeyValueStore(ABC, KeyValueStoreProtocol):
|
|
|
68
68
|
Args:
|
|
69
69
|
config: Redis-specific config parameters.
|
|
70
70
|
key_prefix: Optional prefix for all keys (e.g., "myapp:users:").
|
|
71
|
-
|
|
71
|
+
|
|
72
|
+
You might be able to pass additional arguments specific to subclasses.
|
|
72
73
|
"""
|
|
73
74
|
async with ConfiguredRedisClient(config) as client:
|
|
74
75
|
yield cls(
|
|
@@ -92,9 +93,13 @@ class RedisBaseKeyValueStore(ABC, KeyValueStoreProtocol):
|
|
|
92
93
|
client: An instance of an async Redis client.
|
|
93
94
|
config: Redis-specific config parameters.
|
|
94
95
|
key_prefix: Prefix for all keys to enable logical separation.
|
|
95
|
-
|
|
96
|
+
|
|
97
|
+
You might be able to pass additional arguments specific to subclasses.
|
|
96
98
|
"""
|
|
97
|
-
|
|
99
|
+
config_args = ", ".join(
|
|
100
|
+
f"{k}={v!r}" for k, v in config.model_dump(exclude_defaults=True).items()
|
|
101
|
+
)
|
|
102
|
+
args = [f"config=RedisConfig({config_args})"]
|
|
98
103
|
if key_prefix != DEFAULT_KV_PREFIX:
|
|
99
104
|
args.append(f"key_prefix={key_prefix!r}")
|
|
100
105
|
for key, val in kwargs.items():
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
|
|
2
|
+
# for the German Human Genome-Phenome Archive (GHGA)
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
"""Subpackage containing an S3-based providers and associated utilities."""
|
|
18
|
+
|
|
19
|
+
# shortcuts:
|
|
20
|
+
from hexkit.providers.s3.config import S3Config
|
|
21
|
+
from hexkit.providers.s3.provider import (
|
|
22
|
+
S3BytesKeyValueStore,
|
|
23
|
+
S3DtoKeyValueStore,
|
|
24
|
+
S3JsonKeyValueStore,
|
|
25
|
+
S3ObjectStorage,
|
|
26
|
+
S3StrKeyValueStore,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"S3BytesKeyValueStore",
|
|
31
|
+
"S3Config",
|
|
32
|
+
"S3DtoKeyValueStore",
|
|
33
|
+
"S3JsonKeyValueStore",
|
|
34
|
+
"S3ObjectStorage",
|
|
35
|
+
"S3StrKeyValueStore",
|
|
36
|
+
]
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Copyright 2021 - 2026 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln
|
|
2
|
+
# for the German Human Genome-Phenome Archive (GHGA)
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
"""S3-specific configuration."""
|
|
17
|
+
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
from pydantic import Field, SecretStr
|
|
21
|
+
from pydantic_settings import BaseSettings
|
|
22
|
+
|
|
23
|
+
__all__ = ["S3Config"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class S3Config(BaseSettings):
|
|
27
|
+
"""S3-specific config params.
|
|
28
|
+
|
|
29
|
+
Inherit your config class from this class if you need to talk
|
|
30
|
+
to an S3 service in the backend.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
s3_endpoint_url: str = Field(
|
|
34
|
+
..., examples=["http://localhost:4566"], description="URL to the S3 API."
|
|
35
|
+
)
|
|
36
|
+
s3_access_key_id: str = Field(
|
|
37
|
+
...,
|
|
38
|
+
examples=["my-access-key-id"],
|
|
39
|
+
description=(
|
|
40
|
+
"Part of credentials for login into the S3 service. See:"
|
|
41
|
+
+ " https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html"
|
|
42
|
+
),
|
|
43
|
+
)
|
|
44
|
+
s3_secret_access_key: SecretStr = Field(
|
|
45
|
+
...,
|
|
46
|
+
examples=["my-secret-access-key"],
|
|
47
|
+
description=(
|
|
48
|
+
"Part of credentials for login into the S3 service. See:"
|
|
49
|
+
+ " https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html"
|
|
50
|
+
),
|
|
51
|
+
)
|
|
52
|
+
s3_session_token: SecretStr | None = Field(
|
|
53
|
+
None,
|
|
54
|
+
examples=["my-session-token"],
|
|
55
|
+
description=(
|
|
56
|
+
"Part of credentials for login into the S3 service. See:"
|
|
57
|
+
+ " https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html"
|
|
58
|
+
),
|
|
59
|
+
)
|
|
60
|
+
aws_config_ini: Path | None = Field(
|
|
61
|
+
None,
|
|
62
|
+
examples=["~/.aws/config"],
|
|
63
|
+
description=(
|
|
64
|
+
"Path to a config file for specifying more advanced S3 parameters."
|
|
65
|
+
+ " This should follow the format described here:"
|
|
66
|
+
+ " https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html#using-a-configuration-file"
|
|
67
|
+
),
|
|
68
|
+
)
|
{hexkit-8.1.0/src/hexkit/providers/s3 → hexkit-8.2.0/src/hexkit/providers/s3/provider}/__init__.py
RENAMED
|
@@ -14,11 +14,23 @@
|
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
#
|
|
16
16
|
|
|
17
|
-
"""Subpackage containing
|
|
18
|
-
and associated utilities.
|
|
17
|
+
"""Subpackage containing S3-based providers implementing the ObjectStorageProtocol
|
|
18
|
+
and KeyValueStoreProtocol, and associated utilities.
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
# shortcuts:
|
|
22
|
-
from
|
|
22
|
+
from .kvstore import (
|
|
23
|
+
S3BytesKeyValueStore,
|
|
24
|
+
S3DtoKeyValueStore,
|
|
25
|
+
S3JsonKeyValueStore,
|
|
26
|
+
S3StrKeyValueStore,
|
|
27
|
+
)
|
|
28
|
+
from .objstorage import S3ObjectStorage
|
|
23
29
|
|
|
24
|
-
__all__ = [
|
|
30
|
+
__all__ = [
|
|
31
|
+
"S3BytesKeyValueStore",
|
|
32
|
+
"S3DtoKeyValueStore",
|
|
33
|
+
"S3JsonKeyValueStore",
|
|
34
|
+
"S3ObjectStorage",
|
|
35
|
+
"S3StrKeyValueStore",
|
|
36
|
+
]
|