koi-net 1.0.0b19__py3-none-any.whl → 1.1.0__py3-none-any.whl
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.
Potentially problematic release.
This version of koi-net might be problematic. Click here for more details.
- koi_net/actor.py +60 -0
- koi_net/config.py +44 -18
- koi_net/context.py +63 -0
- koi_net/core.py +152 -84
- koi_net/default_actions.py +15 -0
- koi_net/effector.py +139 -0
- koi_net/identity.py +4 -22
- koi_net/lifecycle.py +104 -0
- koi_net/network/__init__.py +0 -1
- koi_net/network/error_handler.py +50 -0
- koi_net/network/event_queue.py +199 -0
- koi_net/network/graph.py +23 -38
- koi_net/network/request_handler.py +129 -66
- koi_net/network/resolver.py +150 -0
- koi_net/network/response_handler.py +15 -6
- koi_net/poller.py +40 -0
- koi_net/processor/__init__.py +0 -1
- koi_net/processor/default_handlers.py +71 -42
- koi_net/processor/handler.py +3 -7
- koi_net/processor/interface.py +15 -214
- koi_net/processor/knowledge_object.py +10 -17
- koi_net/processor/knowledge_pipeline.py +220 -0
- koi_net/protocol/api_models.py +18 -3
- koi_net/protocol/edge.py +26 -1
- koi_net/protocol/envelope.py +58 -0
- koi_net/protocol/errors.py +23 -0
- koi_net/protocol/event.py +0 -3
- koi_net/protocol/node.py +2 -1
- koi_net/protocol/secure.py +160 -0
- koi_net/secure.py +117 -0
- koi_net/server.py +129 -0
- {koi_net-1.0.0b19.dist-info → koi_net-1.1.0.dist-info}/METADATA +5 -4
- koi_net-1.1.0.dist-info/RECORD +38 -0
- koi_net/network/interface.py +0 -276
- koi_net/protocol/helpers.py +0 -25
- koi_net-1.0.0b19.dist-info/RECORD +0 -25
- {koi_net-1.0.0b19.dist-info → koi_net-1.1.0.dist-info}/WHEEL +0 -0
- {koi_net-1.0.0b19.dist-info → koi_net-1.1.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import httpx
|
|
3
3
|
from rid_lib import RID
|
|
4
|
-
from rid_lib.ext import Cache
|
|
5
4
|
from rid_lib.types.koi_net_node import KoiNetNode
|
|
5
|
+
|
|
6
|
+
from ..identity import NodeIdentity
|
|
6
7
|
from ..protocol.api_models import (
|
|
7
8
|
RidsPayload,
|
|
8
9
|
ManifestsPayload,
|
|
@@ -13,8 +14,10 @@ from ..protocol.api_models import (
|
|
|
13
14
|
FetchBundles,
|
|
14
15
|
PollEvents,
|
|
15
16
|
RequestModels,
|
|
16
|
-
ResponseModels
|
|
17
|
+
ResponseModels,
|
|
18
|
+
ErrorResponse
|
|
17
19
|
)
|
|
20
|
+
from ..protocol.envelope import SignedEnvelope
|
|
18
21
|
from ..protocol.consts import (
|
|
19
22
|
BROADCAST_EVENTS_PATH,
|
|
20
23
|
POLL_EVENTS_PATH,
|
|
@@ -22,128 +25,188 @@ from ..protocol.consts import (
|
|
|
22
25
|
FETCH_MANIFESTS_PATH,
|
|
23
26
|
FETCH_BUNDLES_PATH
|
|
24
27
|
)
|
|
25
|
-
from ..protocol.node import NodeType
|
|
26
|
-
from
|
|
28
|
+
from ..protocol.node import NodeProfile, NodeType
|
|
29
|
+
from ..secure import Secure
|
|
30
|
+
from ..effector import Effector
|
|
31
|
+
|
|
32
|
+
from typing import TYPE_CHECKING
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from .error_handler import ErrorHandler
|
|
27
35
|
|
|
28
36
|
|
|
29
37
|
logger = logging.getLogger(__name__)
|
|
30
38
|
|
|
31
39
|
|
|
40
|
+
# Custom error types for request handling
|
|
41
|
+
class SelfRequestError(Exception):
|
|
42
|
+
"""Raised when a node tries to request itself."""
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
class PartialNodeQueryError(Exception):
|
|
46
|
+
"""Raised when attempting to query a partial node."""
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
class NodeNotFoundError(Exception):
|
|
50
|
+
"""Raised when a node URL cannot be found."""
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
class UnknownPathError(Exception):
|
|
54
|
+
"""Raised when an unknown path is requested."""
|
|
55
|
+
pass
|
|
56
|
+
|
|
32
57
|
class RequestHandler:
|
|
33
58
|
"""Handles making requests to other KOI nodes."""
|
|
34
59
|
|
|
35
|
-
|
|
36
|
-
|
|
60
|
+
effector: Effector
|
|
61
|
+
identity: NodeIdentity
|
|
62
|
+
secure: Secure
|
|
63
|
+
error_handler: "ErrorHandler"
|
|
37
64
|
|
|
38
|
-
def __init__(
|
|
39
|
-
self
|
|
40
|
-
|
|
65
|
+
def __init__(
|
|
66
|
+
self,
|
|
67
|
+
effector: Effector,
|
|
68
|
+
identity: NodeIdentity,
|
|
69
|
+
secure: Secure
|
|
70
|
+
):
|
|
71
|
+
self.effector = effector
|
|
72
|
+
self.identity = identity
|
|
73
|
+
self.secure = secure
|
|
74
|
+
|
|
75
|
+
def set_error_handler(self, error_handler: "ErrorHandler"):
|
|
76
|
+
self.error_handler = error_handler
|
|
77
|
+
|
|
78
|
+
def get_url(self, node_rid: KoiNetNode) -> str:
|
|
79
|
+
"""Retrieves URL of a node."""
|
|
80
|
+
|
|
81
|
+
logger.debug(f"Getting URL for {node_rid!r}")
|
|
82
|
+
node_url = None
|
|
83
|
+
|
|
84
|
+
if node_rid == self.identity.rid:
|
|
85
|
+
raise SelfRequestError("Don't talk to yourself")
|
|
86
|
+
|
|
87
|
+
node_bundle = self.effector.deref(node_rid)
|
|
41
88
|
|
|
89
|
+
if node_bundle:
|
|
90
|
+
node_profile = node_bundle.validate_contents(NodeProfile)
|
|
91
|
+
logger.debug(f"Found node profile: {node_profile}")
|
|
92
|
+
if node_profile.node_type != NodeType.FULL:
|
|
93
|
+
raise PartialNodeQueryError("Can't query partial node")
|
|
94
|
+
node_url = node_profile.base_url
|
|
95
|
+
|
|
96
|
+
else:
|
|
97
|
+
if node_rid == self.identity.config.koi_net.first_contact.rid:
|
|
98
|
+
logger.debug("Found URL of first contact")
|
|
99
|
+
node_url = self.identity.config.koi_net.first_contact.url
|
|
100
|
+
|
|
101
|
+
if not node_url:
|
|
102
|
+
raise NodeNotFoundError("Node not found")
|
|
103
|
+
|
|
104
|
+
logger.debug(f"Resolved {node_rid!r} to {node_url}")
|
|
105
|
+
return node_url
|
|
106
|
+
|
|
42
107
|
def make_request(
|
|
43
|
-
self,
|
|
44
|
-
|
|
108
|
+
self,
|
|
109
|
+
node: KoiNetNode,
|
|
110
|
+
path: str,
|
|
45
111
|
request: RequestModels,
|
|
46
|
-
response_model: type[ResponseModels] | None = None
|
|
47
112
|
) -> ResponseModels | None:
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
113
|
+
url = self.get_url(node) + path
|
|
114
|
+
logger.info(f"Making request to {url}")
|
|
115
|
+
|
|
116
|
+
signed_envelope = self.secure.create_envelope(
|
|
117
|
+
payload=request,
|
|
118
|
+
target=node
|
|
52
119
|
)
|
|
53
|
-
if response_model:
|
|
54
|
-
return response_model.model_validate_json(resp.text)
|
|
55
|
-
|
|
56
|
-
def get_url(self, node_rid: KoiNetNode, url: str) -> str:
|
|
57
|
-
"""Retrieves URL of a node, or returns provided URL."""
|
|
58
120
|
|
|
59
|
-
|
|
60
|
-
|
|
121
|
+
try:
|
|
122
|
+
result = httpx.post(url, data=signed_envelope.model_dump_json(exclude_none=True))
|
|
123
|
+
except httpx.ConnectError as err:
|
|
124
|
+
logger.debug("Failed to connect")
|
|
125
|
+
self.error_handler.handle_connection_error(node)
|
|
126
|
+
raise err
|
|
61
127
|
|
|
62
|
-
if
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
128
|
+
if result.status_code != 200:
|
|
129
|
+
resp = ErrorResponse.model_validate_json(result.text)
|
|
130
|
+
self.error_handler.handle_protocol_error(resp.error, node)
|
|
131
|
+
return resp
|
|
132
|
+
|
|
133
|
+
if path == BROADCAST_EVENTS_PATH:
|
|
134
|
+
return None
|
|
135
|
+
elif path == POLL_EVENTS_PATH:
|
|
136
|
+
EnvelopeModel = SignedEnvelope[EventsPayload]
|
|
137
|
+
elif path == FETCH_RIDS_PATH:
|
|
138
|
+
EnvelopeModel = SignedEnvelope[RidsPayload]
|
|
139
|
+
elif path == FETCH_MANIFESTS_PATH:
|
|
140
|
+
EnvelopeModel = SignedEnvelope[ManifestsPayload]
|
|
141
|
+
elif path == FETCH_BUNDLES_PATH:
|
|
142
|
+
EnvelopeModel = SignedEnvelope[BundlesPayload]
|
|
70
143
|
else:
|
|
71
|
-
|
|
144
|
+
raise UnknownPathError(f"Unknown path '{path}'")
|
|
145
|
+
|
|
146
|
+
resp_envelope = EnvelopeModel.model_validate_json(result.text)
|
|
147
|
+
self.secure.validate_envelope(resp_envelope)
|
|
148
|
+
|
|
149
|
+
return resp_envelope.payload
|
|
72
150
|
|
|
73
151
|
def broadcast_events(
|
|
74
152
|
self,
|
|
75
|
-
node: RID
|
|
76
|
-
url: str = None,
|
|
153
|
+
node: RID,
|
|
77
154
|
req: EventsPayload | None = None,
|
|
78
155
|
**kwargs
|
|
79
156
|
) -> None:
|
|
80
157
|
"""See protocol.api_models.EventsPayload for available kwargs."""
|
|
81
158
|
request = req or EventsPayload.model_validate(kwargs)
|
|
82
|
-
self.make_request(
|
|
83
|
-
|
|
84
|
-
)
|
|
85
|
-
logger.info(f"Broadcasted {len(request.events)} event(s) to {node or url!r}")
|
|
159
|
+
self.make_request(node, BROADCAST_EVENTS_PATH, request)
|
|
160
|
+
logger.info(f"Broadcasted {len(request.events)} event(s) to {node!r}")
|
|
86
161
|
|
|
87
162
|
def poll_events(
|
|
88
163
|
self,
|
|
89
|
-
node: RID
|
|
90
|
-
url: str = None,
|
|
164
|
+
node: RID,
|
|
91
165
|
req: PollEvents | None = None,
|
|
92
166
|
**kwargs
|
|
93
167
|
) -> EventsPayload:
|
|
94
168
|
"""See protocol.api_models.PollEvents for available kwargs."""
|
|
95
169
|
request = req or PollEvents.model_validate(kwargs)
|
|
96
|
-
resp = self.make_request(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
)
|
|
100
|
-
logger.info(f"Polled {len(resp.events)} events from {node or url!r}")
|
|
170
|
+
resp = self.make_request(node, POLL_EVENTS_PATH, request)
|
|
171
|
+
if type(resp) != ErrorResponse:
|
|
172
|
+
logger.info(f"Polled {len(resp.events)} events from {node!r}")
|
|
101
173
|
return resp
|
|
102
174
|
|
|
103
175
|
def fetch_rids(
|
|
104
176
|
self,
|
|
105
|
-
node: RID
|
|
106
|
-
url: str = None,
|
|
177
|
+
node: RID,
|
|
107
178
|
req: FetchRids | None = None,
|
|
108
179
|
**kwargs
|
|
109
180
|
) -> RidsPayload:
|
|
110
181
|
"""See protocol.api_models.FetchRids for available kwargs."""
|
|
111
182
|
request = req or FetchRids.model_validate(kwargs)
|
|
112
|
-
resp = self.make_request(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
)
|
|
116
|
-
logger.info(f"Fetched {len(resp.rids)} RID(s) from {node or url!r}")
|
|
183
|
+
resp = self.make_request(node, FETCH_RIDS_PATH, request)
|
|
184
|
+
if type(resp) != ErrorResponse:
|
|
185
|
+
logger.info(f"Fetched {len(resp.rids)} RID(s) from {node!r}")
|
|
117
186
|
return resp
|
|
118
187
|
|
|
119
188
|
def fetch_manifests(
|
|
120
189
|
self,
|
|
121
|
-
node: RID
|
|
122
|
-
url: str = None,
|
|
190
|
+
node: RID,
|
|
123
191
|
req: FetchManifests | None = None,
|
|
124
192
|
**kwargs
|
|
125
193
|
) -> ManifestsPayload:
|
|
126
194
|
"""See protocol.api_models.FetchManifests for available kwargs."""
|
|
127
195
|
request = req or FetchManifests.model_validate(kwargs)
|
|
128
|
-
resp = self.make_request(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
)
|
|
132
|
-
logger.info(f"Fetched {len(resp.manifests)} manifest(s) from {node or url!r}")
|
|
196
|
+
resp = self.make_request(node, FETCH_MANIFESTS_PATH, request)
|
|
197
|
+
if type(resp) != ErrorResponse:
|
|
198
|
+
logger.info(f"Fetched {len(resp.manifests)} manifest(s) from {node!r}")
|
|
133
199
|
return resp
|
|
134
200
|
|
|
135
201
|
def fetch_bundles(
|
|
136
202
|
self,
|
|
137
|
-
node: RID
|
|
138
|
-
url: str = None,
|
|
203
|
+
node: RID,
|
|
139
204
|
req: FetchBundles | None = None,
|
|
140
205
|
**kwargs
|
|
141
206
|
) -> BundlesPayload:
|
|
142
207
|
"""See protocol.api_models.FetchBundles for available kwargs."""
|
|
143
208
|
request = req or FetchBundles.model_validate(kwargs)
|
|
144
|
-
resp = self.make_request(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
)
|
|
148
|
-
logger.info(f"Fetched {len(resp.bundles)} bundle(s) from {node or url!r}")
|
|
209
|
+
resp = self.make_request(node, FETCH_BUNDLES_PATH, request)
|
|
210
|
+
if type(resp) != ErrorResponse:
|
|
211
|
+
logger.info(f"Fetched {len(resp.bundles)} bundle(s) from {node!r}")
|
|
149
212
|
return resp
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import httpx
|
|
3
|
+
from rid_lib import RID
|
|
4
|
+
from rid_lib.core import RIDType
|
|
5
|
+
from rid_lib.ext import Cache, Bundle
|
|
6
|
+
from rid_lib.types import KoiNetNode
|
|
7
|
+
|
|
8
|
+
from .graph import NetworkGraph
|
|
9
|
+
from .request_handler import RequestHandler
|
|
10
|
+
from ..protocol.node import NodeProfile, NodeType
|
|
11
|
+
from ..protocol.event import Event
|
|
12
|
+
from ..protocol.api_models import ErrorResponse
|
|
13
|
+
from ..identity import NodeIdentity
|
|
14
|
+
from ..config import NodeConfig
|
|
15
|
+
from ..effector import Effector
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class NetworkResolver:
|
|
21
|
+
"""A collection of functions and classes to interact with the KOI network."""
|
|
22
|
+
|
|
23
|
+
config: NodeConfig
|
|
24
|
+
identity: NodeIdentity
|
|
25
|
+
effector: Effector
|
|
26
|
+
cache: Cache
|
|
27
|
+
graph: NetworkGraph
|
|
28
|
+
request_handler: RequestHandler
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
config: NodeConfig,
|
|
33
|
+
cache: Cache,
|
|
34
|
+
identity: NodeIdentity,
|
|
35
|
+
effector: Effector,
|
|
36
|
+
graph: NetworkGraph,
|
|
37
|
+
request_handler: RequestHandler,
|
|
38
|
+
):
|
|
39
|
+
self.config = config
|
|
40
|
+
self.identity = identity
|
|
41
|
+
self.cache = cache
|
|
42
|
+
self.graph = graph
|
|
43
|
+
self.request_handler = request_handler
|
|
44
|
+
self.effector = effector
|
|
45
|
+
|
|
46
|
+
self.poll_event_queue = dict()
|
|
47
|
+
self.webhook_event_queue = dict()
|
|
48
|
+
|
|
49
|
+
def get_state_providers(self, rid_type: RIDType) -> list[KoiNetNode]:
|
|
50
|
+
"""Returns list of node RIDs which provide state for the specified RID type."""
|
|
51
|
+
|
|
52
|
+
logger.debug(f"Looking for state providers of {rid_type}")
|
|
53
|
+
provider_nodes = []
|
|
54
|
+
for node_rid in self.cache.list_rids(rid_types=[KoiNetNode]):
|
|
55
|
+
if node_rid == self.identity.rid:
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
node_bundle = self.cache.read(node_rid)
|
|
59
|
+
|
|
60
|
+
node_profile = node_bundle.validate_contents(NodeProfile)
|
|
61
|
+
|
|
62
|
+
if (node_profile.node_type == NodeType.FULL) and (rid_type in node_profile.provides.state):
|
|
63
|
+
logger.debug(f"Found provider {node_rid!r}")
|
|
64
|
+
provider_nodes.append(node_rid)
|
|
65
|
+
|
|
66
|
+
if not provider_nodes:
|
|
67
|
+
logger.debug("Failed to find providers")
|
|
68
|
+
return provider_nodes
|
|
69
|
+
|
|
70
|
+
def fetch_remote_bundle(self, rid: RID) -> tuple[Bundle | None, KoiNetNode | None]:
|
|
71
|
+
"""Attempts to fetch a bundle by RID from known peer nodes."""
|
|
72
|
+
|
|
73
|
+
logger.debug(f"Fetching remote bundle {rid!r}")
|
|
74
|
+
remote_bundle, node_rid = None, None
|
|
75
|
+
for node_rid in self.get_state_providers(type(rid)):
|
|
76
|
+
payload = self.request_handler.fetch_bundles(
|
|
77
|
+
node=node_rid, rids=[rid])
|
|
78
|
+
|
|
79
|
+
if payload.bundles:
|
|
80
|
+
remote_bundle = payload.bundles[0]
|
|
81
|
+
logger.debug(f"Got bundle from {node_rid!r}")
|
|
82
|
+
break
|
|
83
|
+
|
|
84
|
+
if not remote_bundle:
|
|
85
|
+
logger.warning("Failed to fetch remote bundle")
|
|
86
|
+
|
|
87
|
+
return remote_bundle, node_rid
|
|
88
|
+
|
|
89
|
+
def fetch_remote_manifest(self, rid: RID) -> tuple[Bundle | None, KoiNetNode | None]:
|
|
90
|
+
"""Attempts to fetch a manifest by RID from known peer nodes."""
|
|
91
|
+
|
|
92
|
+
logger.debug(f"Fetching remote manifest {rid!r}")
|
|
93
|
+
remote_manifest, node_rid = None, None
|
|
94
|
+
for node_rid in self.get_state_providers(type(rid)):
|
|
95
|
+
payload = self.request_handler.fetch_manifests(
|
|
96
|
+
node=node_rid, rids=[rid])
|
|
97
|
+
|
|
98
|
+
if payload.manifests:
|
|
99
|
+
remote_manifest = payload.manifests[0]
|
|
100
|
+
logger.debug(f"Got bundle from {node_rid!r}")
|
|
101
|
+
break
|
|
102
|
+
|
|
103
|
+
if not remote_manifest:
|
|
104
|
+
logger.warning("Failed to fetch remote bundle")
|
|
105
|
+
|
|
106
|
+
return remote_manifest, node_rid
|
|
107
|
+
|
|
108
|
+
def poll_neighbors(self) -> dict[KoiNetNode, list[Event]]:
|
|
109
|
+
"""Polls all neighboring nodes and returns compiled list of events.
|
|
110
|
+
|
|
111
|
+
If this node has no neighbors, it will instead attempt to poll the provided first contact URL.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
graph_neighbors = self.graph.get_neighbors()
|
|
115
|
+
neighbors = []
|
|
116
|
+
|
|
117
|
+
if graph_neighbors:
|
|
118
|
+
for node_rid in graph_neighbors:
|
|
119
|
+
node_bundle = self.cache.read(node_rid)
|
|
120
|
+
if not node_bundle:
|
|
121
|
+
continue
|
|
122
|
+
node_profile = node_bundle.validate_contents(NodeProfile)
|
|
123
|
+
if node_profile.node_type != NodeType.FULL:
|
|
124
|
+
continue
|
|
125
|
+
neighbors.append(node_rid)
|
|
126
|
+
|
|
127
|
+
elif self.config.koi_net.first_contact.rid:
|
|
128
|
+
neighbors.append(self.config.koi_net.first_contact.rid)
|
|
129
|
+
|
|
130
|
+
event_dict = dict()
|
|
131
|
+
for node_rid in neighbors:
|
|
132
|
+
try:
|
|
133
|
+
payload = self.request_handler.poll_events(
|
|
134
|
+
node=node_rid,
|
|
135
|
+
rid=self.identity.rid
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
if type(payload) == ErrorResponse:
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
if payload.events:
|
|
142
|
+
logger.debug(f"Received {len(payload.events)} events from {node_rid!r}")
|
|
143
|
+
|
|
144
|
+
event_dict[node_rid] = payload.events
|
|
145
|
+
|
|
146
|
+
except httpx.ConnectError:
|
|
147
|
+
logger.debug(f"Failed to reach node {node_rid!r}")
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
return event_dict
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from rid_lib import RID
|
|
3
|
+
from rid_lib.types import KoiNetNode
|
|
3
4
|
from rid_lib.ext import Manifest, Cache
|
|
4
5
|
from rid_lib.ext.bundle import Bundle
|
|
6
|
+
|
|
5
7
|
from ..protocol.api_models import (
|
|
6
8
|
RidsPayload,
|
|
7
9
|
ManifestsPayload,
|
|
@@ -10,6 +12,7 @@ from ..protocol.api_models import (
|
|
|
10
12
|
FetchManifests,
|
|
11
13
|
FetchBundles,
|
|
12
14
|
)
|
|
15
|
+
from ..effector import Effector
|
|
13
16
|
|
|
14
17
|
logger = logging.getLogger(__name__)
|
|
15
18
|
|
|
@@ -18,24 +21,30 @@ class ResponseHandler:
|
|
|
18
21
|
"""Handles generating responses to requests from other KOI nodes."""
|
|
19
22
|
|
|
20
23
|
cache: Cache
|
|
24
|
+
effector: Effector
|
|
21
25
|
|
|
22
|
-
def __init__(
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
cache: Cache,
|
|
29
|
+
effector: Effector,
|
|
30
|
+
):
|
|
23
31
|
self.cache = cache
|
|
32
|
+
self.effector = effector
|
|
24
33
|
|
|
25
|
-
def fetch_rids(self, req: FetchRids) -> RidsPayload:
|
|
34
|
+
def fetch_rids(self, req: FetchRids, source: KoiNetNode) -> RidsPayload:
|
|
26
35
|
logger.info(f"Request to fetch rids, allowed types {req.rid_types}")
|
|
27
36
|
rids = self.cache.list_rids(req.rid_types)
|
|
28
37
|
|
|
29
38
|
return RidsPayload(rids=rids)
|
|
30
39
|
|
|
31
|
-
def fetch_manifests(self, req: FetchManifests) -> ManifestsPayload:
|
|
40
|
+
def fetch_manifests(self, req: FetchManifests, source: KoiNetNode) -> ManifestsPayload:
|
|
32
41
|
logger.info(f"Request to fetch manifests, allowed types {req.rid_types}, rids {req.rids}")
|
|
33
42
|
|
|
34
43
|
manifests: list[Manifest] = []
|
|
35
44
|
not_found: list[RID] = []
|
|
36
45
|
|
|
37
46
|
for rid in (req.rids or self.cache.list_rids(req.rid_types)):
|
|
38
|
-
bundle = self.
|
|
47
|
+
bundle = self.effector.deref(rid)
|
|
39
48
|
if bundle:
|
|
40
49
|
manifests.append(bundle.manifest)
|
|
41
50
|
else:
|
|
@@ -43,14 +52,14 @@ class ResponseHandler:
|
|
|
43
52
|
|
|
44
53
|
return ManifestsPayload(manifests=manifests, not_found=not_found)
|
|
45
54
|
|
|
46
|
-
def fetch_bundles(self, req: FetchBundles) -> BundlesPayload:
|
|
55
|
+
def fetch_bundles(self, req: FetchBundles, source: KoiNetNode) -> BundlesPayload:
|
|
47
56
|
logger.info(f"Request to fetch bundles, requested rids {req.rids}")
|
|
48
57
|
|
|
49
58
|
bundles: list[Bundle] = []
|
|
50
59
|
not_found: list[RID] = []
|
|
51
60
|
|
|
52
61
|
for rid in req.rids:
|
|
53
|
-
bundle = self.
|
|
62
|
+
bundle = self.effector.deref(rid)
|
|
54
63
|
if bundle:
|
|
55
64
|
bundles.append(bundle)
|
|
56
65
|
else:
|
koi_net/poller.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
import time
|
|
3
|
+
import logging
|
|
4
|
+
from .processor.interface import ProcessorInterface
|
|
5
|
+
from .lifecycle import NodeLifecycle
|
|
6
|
+
from .network.resolver import NetworkResolver
|
|
7
|
+
from .config import NodeConfig
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class NodePoller:
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
processor: ProcessorInterface,
|
|
16
|
+
lifecycle: NodeLifecycle,
|
|
17
|
+
resolver: NetworkResolver,
|
|
18
|
+
config: NodeConfig
|
|
19
|
+
):
|
|
20
|
+
self.processor = processor
|
|
21
|
+
self.lifecycle = lifecycle
|
|
22
|
+
self.resolver = resolver
|
|
23
|
+
self.config = config
|
|
24
|
+
|
|
25
|
+
def poll(self):
|
|
26
|
+
neighbors = self.resolver.poll_neighbors()
|
|
27
|
+
for node_rid in neighbors:
|
|
28
|
+
for event in neighbors[node_rid]:
|
|
29
|
+
self.processor.handle(event=event, source=node_rid)
|
|
30
|
+
self.processor.flush_kobj_queue()
|
|
31
|
+
|
|
32
|
+
def run(self):
|
|
33
|
+
with self.lifecycle.run():
|
|
34
|
+
while True:
|
|
35
|
+
start_time = time.time()
|
|
36
|
+
self.poll()
|
|
37
|
+
elapsed = time.time() - start_time
|
|
38
|
+
sleep_time = self.config.koi_net.polling_interval - elapsed
|
|
39
|
+
if sleep_time > 0:
|
|
40
|
+
time.sleep(sleep_time)
|
koi_net/processor/__init__.py
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .interface import ProcessorInterface
|