hexkit 0.11.0__tar.gz → 1.0.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-1.0.0/MANIFEST.in +1 -0
- {hexkit-0.11.0/src/hexkit.egg-info → hexkit-1.0.0}/PKG-INFO +1 -1
- {hexkit-0.11.0 → hexkit-1.0.0}/pyproject.toml +1 -1
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/providers/akafka/provider.py +49 -2
- hexkit-1.0.0/src/hexkit/providers/akafka/testcontainer.py +177 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/providers/akafka/testutils.py +5 -4
- hexkit-1.0.0/src/hexkit/providers/s3/test_files/test_file1.yaml +3 -0
- hexkit-1.0.0/src/hexkit/providers/s3/test_files/test_file2.yaml +3 -0
- hexkit-1.0.0/src/hexkit/providers/s3/test_files/test_file3.yaml +3 -0
- hexkit-1.0.0/src/hexkit/providers/s3/test_files/test_file4.yaml +3 -0
- {hexkit-0.11.0 → hexkit-1.0.0/src/hexkit.egg-info}/PKG-INFO +1 -1
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit.egg-info/SOURCES.txt +6 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/LICENSE +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/README.md +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/setup.cfg +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/__init__.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/__main__.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/base.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/config.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/custom_types.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/inject.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/protocols/__init__.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/protocols/dao.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/protocols/eventpub.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/protocols/eventsub.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/protocols/objstorage.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/providers/__init__.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/providers/akafka/__init__.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/providers/mongodb/__init__.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/providers/mongodb/provider.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/providers/mongodb/testutils.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/providers/s3/__init__.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/providers/s3/provider.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/providers/s3/test_files/__init__.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/providers/s3/testutils.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/providers/testing/__init__.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/providers/testing/eventpub.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/providers/testing/utils.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit/utils.py +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit.egg-info/dependency_links.txt +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit.egg-info/requires.txt +0 -0
- {hexkit-0.11.0 → hexkit-1.0.0}/src/hexkit.egg-info/top_level.txt +0 -0
hexkit-1.0.0/MANIFEST.in
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include src/hexkit/providers/s3/test_files/*.yaml
|
|
@@ -23,10 +23,12 @@ Require dependencies of the `akafka` extra. See the `setup.cfg`.
|
|
|
23
23
|
|
|
24
24
|
import json
|
|
25
25
|
import logging
|
|
26
|
+
import ssl
|
|
26
27
|
from contextlib import asynccontextmanager
|
|
27
|
-
from typing import Any, Callable, Protocol, TypeVar
|
|
28
|
+
from typing import Any, Callable, Optional, Protocol, TypeVar
|
|
28
29
|
|
|
29
30
|
from aiokafka import AIOKafkaConsumer, AIOKafkaProducer
|
|
31
|
+
from aiokafka.helpers import create_ssl_context
|
|
30
32
|
from pydantic import Field
|
|
31
33
|
from pydantic_settings import BaseSettings
|
|
32
34
|
|
|
@@ -70,6 +72,29 @@ class KafkaConfig(BaseSettings):
|
|
|
70
72
|
examples=[["localhost:9092"]],
|
|
71
73
|
description="A list of connection strings to connect to Kafka bootstrap servers.",
|
|
72
74
|
)
|
|
75
|
+
kafka_security_protocol: Literal["PLAINTEXT", "SSL"] = Field(
|
|
76
|
+
"PLAINTEXT",
|
|
77
|
+
description="Protocol used to communicate with brokers. "
|
|
78
|
+
+ "Valid values are: PLAINTEXT, SSL.",
|
|
79
|
+
)
|
|
80
|
+
kafka_ssl_cafile: str = Field(
|
|
81
|
+
"",
|
|
82
|
+
description="Certificate Authority file path containing certificates"
|
|
83
|
+
+ " used to sign broker certificates. If a CA not specified, the default"
|
|
84
|
+
+ " system CA will be used if found by OpenSSL.",
|
|
85
|
+
)
|
|
86
|
+
kafka_ssl_certfile: str = Field(
|
|
87
|
+
"",
|
|
88
|
+
description="Optional filename of client certificate, as well as any"
|
|
89
|
+
+ " CA certificates needed to establish the certificate's authenticity.",
|
|
90
|
+
)
|
|
91
|
+
kafka_ssl_keyfile: str = Field(
|
|
92
|
+
"", description="Optional filename containing the client private key."
|
|
93
|
+
)
|
|
94
|
+
kafka_ssl_password: str = Field(
|
|
95
|
+
"",
|
|
96
|
+
description="Optional password to be used for the client private key.",
|
|
97
|
+
)
|
|
73
98
|
|
|
74
99
|
|
|
75
100
|
class EventTypeNotFoundError(RuntimeError):
|
|
@@ -84,13 +109,29 @@ def generate_client_id(*, service_name: str, instance_id: str) -> str:
|
|
|
84
109
|
return f"{service_name}.{instance_id}"
|
|
85
110
|
|
|
86
111
|
|
|
112
|
+
def generate_ssl_context(config: KafkaConfig) -> Optional[ssl.SSLContext]:
|
|
113
|
+
"""Generate SSL context for an encrypted SSL connection to Kafka broker."""
|
|
114
|
+
return (
|
|
115
|
+
create_ssl_context(
|
|
116
|
+
cafile=config.kafka_ssl_cafile,
|
|
117
|
+
certfile=config.kafka_ssl_certfile,
|
|
118
|
+
keyfile=config.kafka_ssl_keyfile,
|
|
119
|
+
password=config.kafka_ssl_password,
|
|
120
|
+
)
|
|
121
|
+
if config.kafka_security_protocol == "SSL"
|
|
122
|
+
else None
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
87
126
|
class KafkaProducerCompatible(Protocol):
|
|
88
127
|
"""A python duck type protocol describing an AIOKafkaProducer or equivalent."""
|
|
89
128
|
|
|
90
|
-
def __init__(
|
|
129
|
+
def __init__( # noqa: PLR0913
|
|
91
130
|
self,
|
|
92
131
|
*,
|
|
93
132
|
bootstrap_servers: str,
|
|
133
|
+
security_protocol: str,
|
|
134
|
+
ssl_context: Optional[ssl.SSLContext],
|
|
94
135
|
client_id: str,
|
|
95
136
|
key_serializer: Callable[[Any], bytes],
|
|
96
137
|
value_serializer: Callable[[Any], bytes],
|
|
@@ -149,6 +190,8 @@ class KafkaEventPublisher(EventPublisherProtocol):
|
|
|
149
190
|
|
|
150
191
|
producer = kafka_producer_cls(
|
|
151
192
|
bootstrap_servers=",".join(config.kafka_servers),
|
|
193
|
+
security_protocol=config.kafka_security_protocol,
|
|
194
|
+
ssl_context=generate_ssl_context(config),
|
|
152
195
|
client_id=client_id,
|
|
153
196
|
key_serializer=lambda key: key.encode("ascii"),
|
|
154
197
|
value_serializer=lambda event_value: json.dumps(event_value).encode(
|
|
@@ -220,6 +263,8 @@ class KafkaConsumerCompatible(Protocol):
|
|
|
220
263
|
self,
|
|
221
264
|
*topics: Ascii,
|
|
222
265
|
bootstrap_servers: str,
|
|
266
|
+
security_protocol: str,
|
|
267
|
+
ssl_context: Optional[ssl.SSLContext],
|
|
223
268
|
client_id: str,
|
|
224
269
|
group_id: str,
|
|
225
270
|
auto_offset_reset: Literal["earliest"],
|
|
@@ -297,6 +342,8 @@ class KafkaEventSubscriber(InboundProviderBase):
|
|
|
297
342
|
consumer = kafka_consumer_cls(
|
|
298
343
|
*topics,
|
|
299
344
|
bootstrap_servers=",".join(config.kafka_servers),
|
|
345
|
+
security_protocol=config.kafka_security_protocol,
|
|
346
|
+
ssl_context=generate_ssl_context(config),
|
|
300
347
|
client_id=client_id,
|
|
301
348
|
group_id=config.service_name,
|
|
302
349
|
auto_offset_reset="earliest",
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Copyright 2021 - 2023 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
|
+
"""Improved Kafka test containers."""
|
|
18
|
+
|
|
19
|
+
import tarfile
|
|
20
|
+
import time
|
|
21
|
+
from io import BytesIO
|
|
22
|
+
from ssl import SSLError
|
|
23
|
+
from textwrap import dedent
|
|
24
|
+
from typing import Literal, Optional
|
|
25
|
+
|
|
26
|
+
from kafka import KafkaConsumer
|
|
27
|
+
from kafka.errors import KafkaError, NoBrokersAvailable, UnrecognizedBrokerVersion
|
|
28
|
+
from testcontainers.core.container import DockerContainer
|
|
29
|
+
from testcontainers.core.waiting_utils import wait_container_is_ready
|
|
30
|
+
|
|
31
|
+
__all__ = ["KafkaSSLContainer"]
|
|
32
|
+
|
|
33
|
+
DEFAULT_IMAGE = "confluentinc/cp-kafka:7.5.1"
|
|
34
|
+
|
|
35
|
+
DEFAULT_PORT = 9093 # default port for the Kafka container
|
|
36
|
+
BROKER_PORT = 9092 # auxiliary port for inter broker listener
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class KafkaSSLContainer(DockerContainer):
|
|
40
|
+
"""Kafka container that supports SSL (or actually TLS)."""
|
|
41
|
+
|
|
42
|
+
TC_START_SCRIPT = "/tc-start.sh"
|
|
43
|
+
SECRETS_PATH = "/etc/kafka/secrets"
|
|
44
|
+
|
|
45
|
+
def __init__( # noqa: C901, PLR0912, PLR0913
|
|
46
|
+
self,
|
|
47
|
+
image: str = DEFAULT_IMAGE,
|
|
48
|
+
port: int = DEFAULT_PORT,
|
|
49
|
+
cert: Optional[str] = None,
|
|
50
|
+
key: Optional[str] = None,
|
|
51
|
+
trusted: Optional[str] = None,
|
|
52
|
+
password: Optional[str] = None,
|
|
53
|
+
client_auth: Optional[Literal["requested", "required", "none"]] = None,
|
|
54
|
+
**kwargs,
|
|
55
|
+
) -> None:
|
|
56
|
+
"""Initialize the Kafka SSL container with the given parameters.
|
|
57
|
+
|
|
58
|
+
"cert" must contain the certificate of the broker and if needed also
|
|
59
|
+
intermediate certificates. "key" must contain the private key of the
|
|
60
|
+
broker. If it password protected, "password" must be specified as well.
|
|
61
|
+
"trusted" must contain the trusted certificates. In "client_auth" you can
|
|
62
|
+
specify whether authentication is requested, required or not needed at all.
|
|
63
|
+
"""
|
|
64
|
+
super().__init__(image, **kwargs)
|
|
65
|
+
env = self.with_env
|
|
66
|
+
self.port = port
|
|
67
|
+
ssl = bool(cert or trusted or client_auth)
|
|
68
|
+
protocol = "SSL" if ssl else "PLAINTEXT"
|
|
69
|
+
self.protocol = protocol
|
|
70
|
+
self.with_exposed_ports(port)
|
|
71
|
+
self.broker_port = DEFAULT_PORT if port == BROKER_PORT else BROKER_PORT
|
|
72
|
+
listeners = f"{protocol}://0.0.0.0:{port},BROKER://0.0.0.0:{self.broker_port}"
|
|
73
|
+
protocol_map = f"BROKER:PLAINTEXT,{protocol}:{protocol}"
|
|
74
|
+
env("KAFKA_LISTENERS", listeners)
|
|
75
|
+
env("KAFKA_INTER_BROKER_LISTENER_NAME", "BROKER")
|
|
76
|
+
env("KAFKA_LISTENER_SECURITY_PROTOCOL_MAP", protocol_map)
|
|
77
|
+
env("KAFKA_BROKER_ID", "1")
|
|
78
|
+
env("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR", "1")
|
|
79
|
+
env("KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS", "1")
|
|
80
|
+
env("KAFKA_LOG_FLUSH_INTERVAL_MESSAGES", "10000000")
|
|
81
|
+
env("KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS", "0")
|
|
82
|
+
if ssl:
|
|
83
|
+
if cert:
|
|
84
|
+
cert = cert.strip().replace("\n", "\\n")
|
|
85
|
+
if key:
|
|
86
|
+
key = key.strip().replace("\n", "\\n")
|
|
87
|
+
if password:
|
|
88
|
+
password = password.strip().replace("\n", "\\n")
|
|
89
|
+
if trusted:
|
|
90
|
+
trusted = trusted.strip().replace("\n", "\\n")
|
|
91
|
+
if cert:
|
|
92
|
+
if not cert.startswith("-----BEGIN") or "CERTIFICATE" not in cert:
|
|
93
|
+
raise ValueError("Certificate chain must be in PEM format")
|
|
94
|
+
env("KAFKA_SSL_KEYSTORE_CERTIFICATE_CHAIN", cert)
|
|
95
|
+
if key:
|
|
96
|
+
if not key.startswith("-----BEGIN") or "PRIVATE KEY" not in key:
|
|
97
|
+
raise ValueError("Private key must be in PEM format")
|
|
98
|
+
env("KAFKA_SSL_KEYSTORE_KEY", key)
|
|
99
|
+
if cert or key:
|
|
100
|
+
env("KAFKA_SSL_KEYSTORE_TYPE", "PEM")
|
|
101
|
+
if key and password:
|
|
102
|
+
env("KAFKA_SSL_KEY_PASSWORD", password)
|
|
103
|
+
if trusted:
|
|
104
|
+
if not trusted.startswith("-----BEGIN") or "CERTIFICATE" not in trusted:
|
|
105
|
+
raise ValueError("Trusted certificates must be in PEM format")
|
|
106
|
+
env("KAFKA_SSL_TRUSTSTORE_CERTIFICATES", trusted)
|
|
107
|
+
env("KAFKA_SSL_TRUSTSTORE_TYPE", "PEM")
|
|
108
|
+
if client_auth:
|
|
109
|
+
env("KAFKA_SSL_CLIENT_AUTH", client_auth)
|
|
110
|
+
env("KAFKA_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM", " ")
|
|
111
|
+
|
|
112
|
+
def get_bootstrap_server(self) -> str:
|
|
113
|
+
"""Get the Kafka bootstrap server."""
|
|
114
|
+
host = self.get_container_host_ip()
|
|
115
|
+
port = self.get_exposed_port(self.port)
|
|
116
|
+
return f"{host}:{port}"
|
|
117
|
+
|
|
118
|
+
def start(self) -> "KafkaSSLContainer":
|
|
119
|
+
"""Start the Docker container."""
|
|
120
|
+
script = self.TC_START_SCRIPT
|
|
121
|
+
command = f'sh -c "while [ ! -f {script} ]; do sleep 0.1; done; sh {script}"'
|
|
122
|
+
self.with_command(command)
|
|
123
|
+
super().start()
|
|
124
|
+
self.tc_start()
|
|
125
|
+
self._connect()
|
|
126
|
+
return self
|
|
127
|
+
|
|
128
|
+
@wait_container_is_ready(
|
|
129
|
+
UnrecognizedBrokerVersion, NoBrokersAvailable, KafkaError, ValueError
|
|
130
|
+
) # pyright: ignore
|
|
131
|
+
def _connect(self) -> None:
|
|
132
|
+
bootstrap_server = self.get_bootstrap_server()
|
|
133
|
+
try:
|
|
134
|
+
consumer = KafkaConsumer(
|
|
135
|
+
group_id="test",
|
|
136
|
+
bootstrap_servers=[bootstrap_server],
|
|
137
|
+
security_protocol=self.protocol,
|
|
138
|
+
)
|
|
139
|
+
except SSLError:
|
|
140
|
+
pass # count this as connected
|
|
141
|
+
else:
|
|
142
|
+
if not consumer.bootstrap_connected():
|
|
143
|
+
raise KafkaError("Unable to connect with Kafka container!")
|
|
144
|
+
|
|
145
|
+
def tc_start(self) -> None:
|
|
146
|
+
"""Start the test container."""
|
|
147
|
+
protocol = self.protocol
|
|
148
|
+
host = self.get_container_host_ip()
|
|
149
|
+
port = self.get_exposed_port(self.port)
|
|
150
|
+
listeners = f"{protocol}://{host}:{port},BROKER://127.0.0.1:{self.broker_port}"
|
|
151
|
+
script = f"""
|
|
152
|
+
#!/bin/bash
|
|
153
|
+
c=/etc/confluent/docker
|
|
154
|
+
. $c/bash-config
|
|
155
|
+
export KAFKA_ADVERTISED_LISTENERS={listeners}
|
|
156
|
+
export KAFKA_ZOOKEEPER_CONNECT=localhost:2181
|
|
157
|
+
p=zookeeper.properties
|
|
158
|
+
echo "clientPort=2181" > $p
|
|
159
|
+
echo "dataDir=/var/lib/zookeeper/data" >> $p
|
|
160
|
+
echo "dataLogDir=/var/lib/zookeeper/log" >> $p
|
|
161
|
+
zookeeper-server-start $p &
|
|
162
|
+
# workaround for https://github.com/confluentinc/kafka-images/issues/244
|
|
163
|
+
sed -i -E '/^if .*LISTENERS.*SSL:/,/^fi/d' $c/configure
|
|
164
|
+
$c/configure && $c/launch
|
|
165
|
+
"""
|
|
166
|
+
self.create_file(dedent(script).strip().encode("utf-8"), self.TC_START_SCRIPT)
|
|
167
|
+
|
|
168
|
+
def create_file(self, content: bytes, path: str) -> None:
|
|
169
|
+
"""Create a file inside the container."""
|
|
170
|
+
with BytesIO() as archive:
|
|
171
|
+
with tarfile.TarFile(fileobj=archive, mode="w") as tar:
|
|
172
|
+
tarinfo = tarfile.TarInfo(name=path)
|
|
173
|
+
tarinfo.size = len(content)
|
|
174
|
+
tarinfo.mtime = time.time() # type: ignore
|
|
175
|
+
tar.addfile(tarinfo, BytesIO(content))
|
|
176
|
+
archive.seek(0)
|
|
177
|
+
self.get_wrapped_container().put_archive("/", archive)
|
|
@@ -38,6 +38,7 @@ from hexkit.providers.akafka.provider import (
|
|
|
38
38
|
KafkaEventPublisher,
|
|
39
39
|
get_event_type,
|
|
40
40
|
)
|
|
41
|
+
from hexkit.providers.akafka.testcontainer import DEFAULT_IMAGE as KAFKA_IMAGE
|
|
41
42
|
|
|
42
43
|
|
|
43
44
|
@dataclass(frozen=True)
|
|
@@ -61,7 +62,7 @@ class ExpectedEvent(EventBase):
|
|
|
61
62
|
|
|
62
63
|
@dataclass(frozen=True)
|
|
63
64
|
class RecordedEvent(EventBase):
|
|
64
|
-
"""Used by the
|
|
65
|
+
"""Used by the EventRecorder class to describe events recorded in a specific topic."""
|
|
65
66
|
|
|
66
67
|
key: Ascii
|
|
67
68
|
|
|
@@ -324,7 +325,7 @@ class EventRecorder:
|
|
|
324
325
|
return self
|
|
325
326
|
|
|
326
327
|
async def __aexit__(self, error_type, error_val, error_tb):
|
|
327
|
-
"""Stop recording and check the recorded events
|
|
328
|
+
"""Stop recording and check the recorded events against the expectation when
|
|
328
329
|
exiting the context block.
|
|
329
330
|
"""
|
|
330
331
|
await self.stop_recording()
|
|
@@ -403,9 +404,9 @@ async def kafka_fixture_function() -> AsyncGenerator[KafkaFixture, None]:
|
|
|
403
404
|
|
|
404
405
|
**Do not call directly** Instead, use get_kafka_fixture()
|
|
405
406
|
"""
|
|
406
|
-
with KafkaContainer(image=
|
|
407
|
+
with KafkaContainer(image=KAFKA_IMAGE) as kafka:
|
|
407
408
|
kafka_servers = [kafka.get_bootstrap_server()]
|
|
408
|
-
config = KafkaConfig(
|
|
409
|
+
config = KafkaConfig( # type: ignore
|
|
409
410
|
service_name="test_publisher",
|
|
410
411
|
service_instance_id="001",
|
|
411
412
|
kafka_servers=kafka_servers,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
2
3
|
README.md
|
|
3
4
|
pyproject.toml
|
|
4
5
|
src/hexkit/__init__.py
|
|
@@ -21,6 +22,7 @@ src/hexkit/protocols/objstorage.py
|
|
|
21
22
|
src/hexkit/providers/__init__.py
|
|
22
23
|
src/hexkit/providers/akafka/__init__.py
|
|
23
24
|
src/hexkit/providers/akafka/provider.py
|
|
25
|
+
src/hexkit/providers/akafka/testcontainer.py
|
|
24
26
|
src/hexkit/providers/akafka/testutils.py
|
|
25
27
|
src/hexkit/providers/mongodb/__init__.py
|
|
26
28
|
src/hexkit/providers/mongodb/provider.py
|
|
@@ -29,6 +31,10 @@ src/hexkit/providers/s3/__init__.py
|
|
|
29
31
|
src/hexkit/providers/s3/provider.py
|
|
30
32
|
src/hexkit/providers/s3/testutils.py
|
|
31
33
|
src/hexkit/providers/s3/test_files/__init__.py
|
|
34
|
+
src/hexkit/providers/s3/test_files/test_file1.yaml
|
|
35
|
+
src/hexkit/providers/s3/test_files/test_file2.yaml
|
|
36
|
+
src/hexkit/providers/s3/test_files/test_file3.yaml
|
|
37
|
+
src/hexkit/providers/s3/test_files/test_file4.yaml
|
|
32
38
|
src/hexkit/providers/testing/__init__.py
|
|
33
39
|
src/hexkit/providers/testing/eventpub.py
|
|
34
40
|
src/hexkit/providers/testing/utils.py
|
|
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
|
|
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
|