hexkit 7.2.1__tar.gz → 7.3.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.
Files changed (79) hide show
  1. {hexkit-7.2.1/src/hexkit.egg-info → hexkit-7.3.0}/PKG-INFO +25 -16
  2. {hexkit-7.2.1 → hexkit-7.3.0}/pyproject.toml +28 -16
  3. hexkit-7.3.0/src/hexkit/protocols/kvstore.py +55 -0
  4. hexkit-7.3.0/src/hexkit/providers/mongodb/__init__.py +41 -0
  5. hexkit-7.3.0/src/hexkit/providers/mongodb/config.py +55 -0
  6. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/mongodb/migrations/_manager.py +1 -1
  7. hexkit-7.3.0/src/hexkit/providers/mongodb/provider/__init__.py +52 -0
  8. hexkit-7.3.0/src/hexkit/providers/mongodb/provider/client.py +102 -0
  9. hexkit-7.2.1/src/hexkit/providers/mongodb/provider.py → hexkit-7.3.0/src/hexkit/providers/mongodb/provider/dao.py +15 -162
  10. hexkit-7.3.0/src/hexkit/providers/mongodb/provider/kvstore.py +230 -0
  11. hexkit-7.3.0/src/hexkit/providers/mongodb/provider/utils.py +66 -0
  12. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/mongodb/testutils.py +4 -7
  13. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/mongokafka/__init__.py +1 -1
  14. {hexkit-7.2.1/src/hexkit/providers/mongokafka/provider → hexkit-7.3.0/src/hexkit/providers/mongokafka}/config.py +1 -1
  15. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/mongokafka/provider/__init__.py +2 -3
  16. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/mongokafka/provider/daopub.py +2 -2
  17. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/mongokafka/provider/persistent_pub.py +6 -1
  18. hexkit-7.3.0/src/hexkit/providers/redis/__init__.py +35 -0
  19. hexkit-7.3.0/src/hexkit/providers/redis/config.py +48 -0
  20. {hexkit-7.2.1/src/hexkit/providers/mongodb → hexkit-7.3.0/src/hexkit/providers/redis/provider}/__init__.py +17 -4
  21. hexkit-7.3.0/src/hexkit/providers/redis/provider/client.py +53 -0
  22. hexkit-7.3.0/src/hexkit/providers/redis/provider/kvstore.py +250 -0
  23. hexkit-7.3.0/src/hexkit/providers/redis/testutils.py +147 -0
  24. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/s3/provider.py +1 -1
  25. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/s3/testutils/_utils.py +5 -5
  26. hexkit-7.3.0/src/hexkit/providers/testing/dao.py +475 -0
  27. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/utils.py +9 -1
  28. {hexkit-7.2.1 → hexkit-7.3.0/src/hexkit.egg-info}/PKG-INFO +25 -16
  29. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit.egg-info/SOURCES.txt +14 -2
  30. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit.egg-info/requires.txt +27 -15
  31. hexkit-7.2.1/src/hexkit/providers/testing/dao.py +0 -146
  32. {hexkit-7.2.1 → hexkit-7.3.0}/LICENSE +0 -0
  33. {hexkit-7.2.1 → hexkit-7.3.0}/MANIFEST.in +0 -0
  34. {hexkit-7.2.1 → hexkit-7.3.0}/README.md +0 -0
  35. {hexkit-7.2.1 → hexkit-7.3.0}/setup.cfg +0 -0
  36. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/__init__.py +0 -0
  37. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/__main__.py +0 -0
  38. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/base.py +0 -0
  39. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/config.py +0 -0
  40. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/correlation.py +0 -0
  41. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/custom_types.py +0 -0
  42. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/log.py +0 -0
  43. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/opentelemetry.py +0 -0
  44. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/protocols/__init__.py +0 -0
  45. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/protocols/dao.py +0 -0
  46. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/protocols/daopub.py +0 -0
  47. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/protocols/daosub.py +0 -0
  48. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/protocols/eventpub.py +0 -0
  49. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/protocols/eventsub.py +0 -0
  50. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/protocols/objstorage.py +0 -0
  51. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/__init__.py +0 -0
  52. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/akafka/__init__.py +0 -0
  53. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/akafka/config.py +0 -0
  54. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/akafka/provider/__init__.py +0 -0
  55. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/akafka/provider/daosub.py +0 -0
  56. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/akafka/provider/eventpub.py +0 -0
  57. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/akafka/provider/eventsub.py +0 -0
  58. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/akafka/provider/utils.py +0 -0
  59. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/akafka/testcontainer.py +0 -0
  60. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/akafka/testutils.py +0 -0
  61. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/mongodb/migrations/__init__.py +0 -0
  62. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/mongodb/migrations/_utils.py +0 -0
  63. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/mongodb/migrations/helpers.py +0 -0
  64. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/mongokafka/testutils.py +0 -0
  65. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/s3/__init__.py +0 -0
  66. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/s3/test_files/__init__.py +0 -0
  67. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/s3/test_files/test_file1.yaml +0 -0
  68. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/s3/test_files/test_file2.yaml +0 -0
  69. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/s3/test_files/test_file3.yaml +0 -0
  70. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/s3/test_files/test_file4.yaml +0 -0
  71. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/s3/testutils/__init__.py +0 -0
  72. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/s3/testutils/_fixtures.py +0 -0
  73. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/s3/testutils/_typical_workflow.py +0 -0
  74. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/testing/__init__.py +0 -0
  75. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/testing/eventpub.py +0 -0
  76. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/providers/testing/s3.py +0 -0
  77. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit/py.typed +0 -0
  78. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit.egg-info/dependency_links.txt +0 -0
  79. {hexkit-7.2.1 → hexkit-7.3.0}/src/hexkit.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hexkit
3
- Version: 7.2.1
3
+ Version: 7.3.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
@@ -18,39 +18,45 @@ Classifier: Intended Audience :: Developers
18
18
  Requires-Python: >=3.10
19
19
  Description-Content-Type: text/markdown
20
20
  License-File: LICENSE
21
- Requires-Dist: opentelemetry-api<2,>=1.37
22
- Requires-Dist: pydantic<3,>=2.11
23
- Requires-Dist: pydantic_settings<3,>=2.10
21
+ Requires-Dist: opentelemetry-api<2,>=1.39
22
+ Requires-Dist: pydantic<3,>=2.12
23
+ Requires-Dist: pydantic_settings<3,>=2.12
24
24
  Requires-Dist: PyYAML<7,>=6
25
25
  Provides-Extra: akafka
26
- Requires-Dist: aiokafka[lz4,snappy,zstd]~=0.12.0; extra == "akafka"
26
+ Requires-Dist: aiokafka[lz4,snappy,zstd]~=0.13.0; extra == "akafka"
27
27
  Requires-Dist: jsonschema<5,>=4.25; extra == "akafka"
28
28
  Provides-Extra: s3
29
- Requires-Dist: boto3<2,>=1.40; extra == "s3"
30
- Requires-Dist: botocore<2,>=1.40; extra == "s3"
29
+ Requires-Dist: boto3<2,>=1.42; extra == "s3"
30
+ Requires-Dist: botocore<2,>=1.42; extra == "s3"
31
31
  Provides-Extra: mongodb
32
32
  Requires-Dist: pymongo<5,>=4.15; extra == "mongodb"
33
+ Provides-Extra: redis
34
+ Requires-Dist: redis<8,>=7.1; extra == "redis"
33
35
  Provides-Extra: opentelemetry-base
34
- Requires-Dist: opentelemetry-sdk<2,>=1.37.0; extra == "opentelemetry-base"
35
- Requires-Dist: opentelemetry-exporter-otlp<2,>=1.37.0; extra == "opentelemetry-base"
36
- Requires-Dist: opentelemetry-instrumentation>=0.58b0; extra == "opentelemetry-base"
37
- Requires-Dist: opentelemetry-instrumentation-httpx>=0.58b0; extra == "opentelemetry-base"
38
- Requires-Dist: opentelemetry-distro>=0.58b0; extra == "opentelemetry-base"
36
+ Requires-Dist: opentelemetry-sdk<2,>=1.39.1; extra == "opentelemetry-base"
37
+ Requires-Dist: opentelemetry-exporter-otlp<2,>=1.39.1; extra == "opentelemetry-base"
38
+ Requires-Dist: opentelemetry-instrumentation>=0.60b1; extra == "opentelemetry-base"
39
+ Requires-Dist: opentelemetry-instrumentation-httpx>=0.60b1; extra == "opentelemetry-base"
40
+ Requires-Dist: opentelemetry-distro>=0.60b1; extra == "opentelemetry-base"
39
41
  Provides-Extra: opentelemetry-akafka
40
42
  Requires-Dist: hexkit[opentelemetry-base]; extra == "opentelemetry-akafka"
41
43
  Requires-Dist: hexkit[akafka]; extra == "opentelemetry-akafka"
42
- Requires-Dist: opentelemetry-instrumentation-aiokafka>=0.58b0; extra == "opentelemetry-akafka"
44
+ Requires-Dist: opentelemetry-instrumentation-aiokafka>=0.60b1; extra == "opentelemetry-akafka"
43
45
  Provides-Extra: opentelemetry-mongodb
44
46
  Requires-Dist: hexkit[opentelemetry-base]; extra == "opentelemetry-mongodb"
45
47
  Requires-Dist: hexkit[mongodb]; extra == "opentelemetry-mongodb"
46
- Requires-Dist: opentelemetry-instrumentation-pymongo>=0.58b0; extra == "opentelemetry-mongodb"
48
+ Requires-Dist: opentelemetry-instrumentation-pymongo>=0.60b1; extra == "opentelemetry-mongodb"
47
49
  Provides-Extra: opentelemetry-fastapi
48
50
  Requires-Dist: hexkit[opentelemetry-base]; extra == "opentelemetry-fastapi"
49
- Requires-Dist: opentelemetry-instrumentation-fastapi>=0.58b0; extra == "opentelemetry-fastapi"
51
+ Requires-Dist: opentelemetry-instrumentation-fastapi>=0.60b1; extra == "opentelemetry-fastapi"
50
52
  Provides-Extra: opentelemetry-s3
51
53
  Requires-Dist: hexkit[opentelemetry-base]; extra == "opentelemetry-s3"
52
54
  Requires-Dist: hexkit[s3]; extra == "opentelemetry-s3"
53
- Requires-Dist: opentelemetry-instrumentation-botocore>=0.58b0; extra == "opentelemetry-s3"
55
+ Requires-Dist: opentelemetry-instrumentation-botocore>=0.60b1; extra == "opentelemetry-s3"
56
+ Provides-Extra: opentelemetry-redis
57
+ Requires-Dist: hexkit[opentelemetry-base]; extra == "opentelemetry-redis"
58
+ Requires-Dist: hexkit[redis]; extra == "opentelemetry-redis"
59
+ Requires-Dist: opentelemetry-instrumentation-redis>=0.60b1; extra == "opentelemetry-redis"
54
60
  Provides-Extra: opentelemetry
55
61
  Requires-Dist: hexkit[opentelemetry-akafka]; extra == "opentelemetry"
56
62
  Requires-Dist: hexkit[opentelemetry-mongodb]; extra == "opentelemetry"
@@ -65,6 +71,9 @@ Requires-Dist: testcontainers<5,>=4.13; extra == "test-s3"
65
71
  Provides-Extra: test-mongodb
66
72
  Requires-Dist: hexkit[mongodb]; extra == "test-mongodb"
67
73
  Requires-Dist: testcontainers[mongo]<5,>=4.13; extra == "test-mongodb"
74
+ Provides-Extra: test-redis
75
+ Requires-Dist: hexkit[redis]; extra == "test-redis"
76
+ Requires-Dist: testcontainers[redis]<5,>=4.13; extra == "test-redis"
68
77
  Provides-Extra: test
69
78
  Requires-Dist: hexkit[test-akafka,test-mongodb,test-s3]; extra == "test"
70
79
  Provides-Extra: all
@@ -23,12 +23,12 @@ classifiers = [
23
23
  "Intended Audience :: Developers",
24
24
  ]
25
25
  name = "hexkit"
26
- version = "7.2.1"
26
+ version = "7.3.0"
27
27
  description = "A Toolkit for Building Microservices using the Hexagonal Architecture"
28
28
  dependencies = [
29
- "opentelemetry-api >=1.37, <2",
30
- "pydantic >=2.11, <3",
31
- "pydantic_settings >=2.10, <3",
29
+ "opentelemetry-api >=1.39, <2",
30
+ "pydantic >=2.12, <3",
31
+ "pydantic_settings >=2.12, <3",
32
32
  "PyYAML >=6, <7",
33
33
  ]
34
34
 
@@ -37,41 +37,49 @@ text = "Apache 2.0"
37
37
 
38
38
  [project.optional-dependencies]
39
39
  akafka = [
40
- "aiokafka[zstd,lz4,snappy]~=0.12.0",
40
+ "aiokafka[zstd,lz4,snappy]~=0.13.0",
41
41
  "jsonschema >=4.25, <5",
42
42
  ]
43
43
  s3 = [
44
- "boto3 >=1.40, <2",
45
- "botocore >=1.40, <2",
44
+ "boto3 >=1.42, <2",
45
+ "botocore >=1.42, <2",
46
46
  ]
47
47
  mongodb = [
48
48
  "pymongo >=4.15, <5",
49
49
  ]
50
+ redis = [
51
+ "redis >=7.1, <8",
52
+ ]
50
53
  opentelemetry-base = [
51
- "opentelemetry-sdk >=1.37.0, <2",
52
- "opentelemetry-exporter-otlp >=1.37.0, <2",
53
- "opentelemetry-instrumentation >=0.58b0",
54
- "opentelemetry-instrumentation-httpx >=0.58b0",
55
- "opentelemetry-distro >=0.58b0",
54
+ "opentelemetry-sdk >=1.39.1, <2",
55
+ "opentelemetry-exporter-otlp >=1.39.1, <2",
56
+ "opentelemetry-instrumentation >=0.60b1",
57
+ "opentelemetry-instrumentation-httpx >=0.60b1",
58
+ "opentelemetry-distro >=0.60b1",
56
59
  ]
57
60
  opentelemetry-akafka = [
58
61
  "hexkit[opentelemetry-base]",
59
62
  "hexkit[akafka]",
60
- "opentelemetry-instrumentation-aiokafka >=0.58b0",
63
+ "opentelemetry-instrumentation-aiokafka >=0.60b1",
61
64
  ]
62
65
  opentelemetry-mongodb = [
63
66
  "hexkit[opentelemetry-base]",
64
67
  "hexkit[mongodb]",
65
- "opentelemetry-instrumentation-pymongo >=0.58b0",
68
+ "opentelemetry-instrumentation-pymongo >=0.60b1",
66
69
  ]
67
70
  opentelemetry-fastapi = [
68
71
  "hexkit[opentelemetry-base]",
69
- "opentelemetry-instrumentation-fastapi >=0.58b0",
72
+ "opentelemetry-instrumentation-fastapi >=0.60b1",
70
73
  ]
71
74
  opentelemetry-s3 = [
72
75
  "hexkit[opentelemetry-base]",
73
76
  "hexkit[s3]",
74
- "opentelemetry-instrumentation-botocore >=0.58b0",
77
+ "opentelemetry-instrumentation-botocore >=0.60b1",
78
+ ]
79
+ opentelemetry-redis = [
80
+ "hexkit[opentelemetry-base]",
81
+ "hexkit[redis]",
82
+ "opentelemetry-instrumentation-redis >=0.60b1",
75
83
  ]
76
84
  opentelemetry = [
77
85
  "hexkit[opentelemetry-akafka]",
@@ -91,6 +99,10 @@ test-mongodb = [
91
99
  "hexkit[mongodb]",
92
100
  "testcontainers[mongo] >=4.13, <5",
93
101
  ]
102
+ test-redis = [
103
+ "hexkit[redis]",
104
+ "testcontainers[redis] >=4.13, <5",
105
+ ]
94
106
  test = [
95
107
  "hexkit[test-akafka,test-s3,test-mongodb]",
96
108
  ]
@@ -0,0 +1,55 @@
1
+ # Copyright 2021 - 2025 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
+ """Protocol supporting key-value store operations."""
18
+
19
+ from typing import Generic, Protocol, TypeVar
20
+
21
+ V = TypeVar("V")
22
+
23
+
24
+ class KeyValueStoreProtocol(Protocol, Generic[V]):
25
+ """A protocol for a key-value store."""
26
+
27
+ async def get(self, key: str, default: V | None = None) -> V | None:
28
+ """Retrieve the value for the given key.
29
+
30
+ Returns the specified default value if there is no such value in the store.
31
+ """
32
+ pass
33
+
34
+ async def set(self, key: str, value: V) -> None:
35
+ """Set the value for the given key.
36
+
37
+ Providers must reject storing a value of None if they
38
+ support a value domain V that can contain such values.
39
+ """
40
+ pass
41
+
42
+ async def delete(self, key: str) -> None:
43
+ """Delete the value for the given key.
44
+
45
+ Does nothing if there is no such value in the store.
46
+ """
47
+ pass
48
+
49
+ async def exists(self, key: str) -> bool:
50
+ """Check if the given key exists.
51
+
52
+ Default implementation for classes inheriting from this protocol.
53
+ Providers can use something more efficient.
54
+ """
55
+ return (await self.get(key)) is not None
@@ -0,0 +1,41 @@
1
+ # Copyright 2021 - 2025 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 MongoDB-based providers and related utilities."""
18
+
19
+ from .config import MongoDbConfig
20
+ from .provider import (
21
+ ConfiguredMongoClient,
22
+ MongoDbBytesKeyValueStore,
23
+ MongoDbDao,
24
+ MongoDbDaoFactory,
25
+ MongoDbDtoKeyValueStore,
26
+ MongoDbJsonKeyValueStore,
27
+ MongoDbStrKeyValueStore,
28
+ translate_pymongo_errors,
29
+ )
30
+
31
+ __all__ = [
32
+ "ConfiguredMongoClient",
33
+ "MongoDbBytesKeyValueStore",
34
+ "MongoDbConfig",
35
+ "MongoDbDao",
36
+ "MongoDbDaoFactory",
37
+ "MongoDbDtoKeyValueStore",
38
+ "MongoDbJsonKeyValueStore",
39
+ "MongoDbStrKeyValueStore",
40
+ "translate_pymongo_errors",
41
+ ]
@@ -0,0 +1,55 @@
1
+ # Copyright 2021 - 2025 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
+ """MongoDB specific configuration."""
18
+
19
+ from pydantic import Field, MongoDsn, PositiveInt, Secret
20
+ from pydantic_settings import BaseSettings
21
+
22
+ __all__ = ["MongoDbConfig"]
23
+
24
+
25
+ class MongoDbConfig(BaseSettings):
26
+ """Configuration parameters for connecting to a MongoDB server.
27
+
28
+ Inherit your config class from this class if your application uses MongoDB.
29
+ """
30
+
31
+ mongo_dsn: Secret[MongoDsn] = Field(
32
+ ...,
33
+ examples=["mongodb://localhost:27017"],
34
+ description=(
35
+ "MongoDB connection string. Might include credentials."
36
+ + " For more information see:"
37
+ + " https://naiveskill.com/mongodb-connection-string/"
38
+ ),
39
+ )
40
+ db_name: str = Field(
41
+ ...,
42
+ examples=["my-database"],
43
+ description="Name of the database located on the MongoDB server.",
44
+ )
45
+ mongo_timeout: PositiveInt | None = Field(
46
+ default=None,
47
+ examples=[300, 600, None],
48
+ description=(
49
+ "Timeout in seconds for API calls to MongoDB. The timeout applies to all steps"
50
+ + " needed to complete the operation, including server selection, connection"
51
+ + " checkout, serialization, and server-side execution. When the timeout"
52
+ + " expires, PyMongo raises a timeout exception. If set to None, the"
53
+ + " operation will not time out (default MongoDB behavior)."
54
+ ),
55
+ )
@@ -27,7 +27,7 @@ from pymongo import AsyncMongoClient
27
27
  from pymongo.asynchronous.database import AsyncDatabase
28
28
  from pymongo.errors import DuplicateKeyError
29
29
 
30
- from hexkit.providers.mongodb import MongoDbConfig
30
+ from hexkit.providers.mongodb.config import MongoDbConfig
31
31
  from hexkit.providers.mongodb.provider import ConfiguredMongoClient
32
32
 
33
33
  from ._utils import MigrationDefinition, Reversible
@@ -0,0 +1,52 @@
1
+ # Copyright 2021 - 2025 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
+ """A subpackage containing all MongoDB-specific providers.
18
+
19
+ Require dependencies of the `mongodb` extra.
20
+ """
21
+
22
+ from .client import ConfiguredMongoClient
23
+ from .dao import (
24
+ MongoDbDao,
25
+ MongoDbDaoFactory,
26
+ get_single_hit,
27
+ replace_id_field_in_find_mapping,
28
+ validate_find_mapping,
29
+ )
30
+ from .kvstore import (
31
+ MongoDbBytesKeyValueStore,
32
+ MongoDbDtoKeyValueStore,
33
+ MongoDbJsonKeyValueStore,
34
+ MongoDbStrKeyValueStore,
35
+ )
36
+ from .utils import document_to_dto, dto_to_document, translate_pymongo_errors
37
+
38
+ __all__ = [
39
+ "ConfiguredMongoClient",
40
+ "MongoDbBytesKeyValueStore",
41
+ "MongoDbDao",
42
+ "MongoDbDaoFactory",
43
+ "MongoDbDtoKeyValueStore",
44
+ "MongoDbJsonKeyValueStore",
45
+ "MongoDbStrKeyValueStore",
46
+ "document_to_dto",
47
+ "dto_to_document",
48
+ "get_single_hit",
49
+ "replace_id_field_in_find_mapping",
50
+ "translate_pymongo_errors",
51
+ "validate_find_mapping",
52
+ ]
@@ -0,0 +1,102 @@
1
+ # Copyright 2021 - 2025 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
+ """Client used for the MongoDB providers."""
17
+
18
+ from typing import TypeVar
19
+
20
+ from pymongo import AsyncMongoClient, MongoClient
21
+
22
+ from hexkit.providers.mongodb.config import MongoDbConfig
23
+
24
+ ClientType = TypeVar("ClientType", AsyncMongoClient, MongoClient)
25
+
26
+ __all__ = ["ConfiguredMongoClient"]
27
+
28
+
29
+ class ConfiguredMongoClient:
30
+ """A context manager for a configured MongoDB client, sync or async.
31
+
32
+ Usage:
33
+ ```python
34
+ from hexkit.providers.mongodb import MongoDbConfig, ConfiguredMongoClient
35
+
36
+ with ConfiguredMongoClient(config=MongoDbConfig(...)) as client:
37
+ # Use the client here
38
+ pass
39
+
40
+ async with ConfiguredMongoClient(config=MongoDbConfig(...)) as client:
41
+ # Use the async client here
42
+ pass
43
+
44
+ # Client is automatically closed after exiting the context manager
45
+ """
46
+
47
+ config: MongoDbConfig
48
+
49
+ def __init__(self, *, config: MongoDbConfig):
50
+ self._config = config
51
+
52
+ async def __aenter__(self) -> AsyncMongoClient:
53
+ """Enter a context manager and return the _asynchronous_ MongoDB client."""
54
+ self._async_client = self.get_client(
55
+ config=self._config, client_cls=AsyncMongoClient
56
+ )
57
+ return self._async_client
58
+
59
+ async def __aexit__(self, exc_type_, exc_value, exc_tb):
60
+ """Close the async MongoDB client."""
61
+ await self._async_client.close()
62
+
63
+ def __enter__(self) -> MongoClient:
64
+ """Enter a context manager and return the _synchronous_ MongoDB client."""
65
+ self._client = self.get_client(config=self._config, client_cls=MongoClient)
66
+ return self._client
67
+
68
+ def __exit__(self, exc_type_, exc_value, exc_tb):
69
+ """Close the synchronous MongoDB client."""
70
+ self._client.close()
71
+
72
+ @classmethod
73
+ def get_client(
74
+ cls,
75
+ *,
76
+ config: MongoDbConfig,
77
+ client_cls: type[ClientType],
78
+ ) -> ClientType:
79
+ """Creates a configured MongoDB client based on the provided configuration,
80
+ with the timeout, uuid representation, and timezone awareness set.
81
+
82
+ *Does not* automatically close the client!
83
+
84
+ Args:
85
+ config: MongoDB-specific configuration parameters.
86
+ client_cls: The class of the MongoDB client to instantiate.
87
+
88
+ Returns:
89
+ A MongoDB client instance configured with the provided parameters.
90
+ """
91
+ timeout_ms = (
92
+ int(config.mongo_timeout * 1000)
93
+ if config.mongo_timeout is not None
94
+ else None
95
+ )
96
+
97
+ return client_cls(
98
+ str(config.mongo_dsn.get_secret_value()),
99
+ timeoutMS=timeout_ms,
100
+ uuidRepresentation="standard",
101
+ tz_aware=True,
102
+ )