Skip to content

Commit eeeffa7

Browse files
Add uuid to service info
1 parent 7463a1f commit eeeffa7

20 files changed

Lines changed: 182 additions & 82 deletions

examples/cameraServiceExample.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from uuid import uuid4
2+
13
from context_logger import get_logger, setup_logging
24

35
from examples import setup_shutdown
@@ -15,7 +17,7 @@ def main() -> None:
1517
group = Group(name='effectiverange/sniper', url='udp://239.0.1.1:5555')
1618

1719
# Define the service information for the camera
18-
info = ServiceInfo(name='er-sniper-camera-1', role='camera', urls={
20+
info = ServiceInfo(uuid=uuid4(), name='er-sniper-camera-1', role='camera', urls={
1921
'device-api': 'grpc://er-sniper-camera-1/device',
2022
'video-stream': 'blob:http://er-sniper-camera-1/video'
2123
})

hello/advertizer.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky <ferenj@effective-range.com>
2+
# SPDX-FileCopyrightText: 2024 Attila Gombos <attila.gombos@effective-range.com>
3+
# SPDX-License-Identifier: MIT
4+
15
import random
26
import time
37
from typing import Any

hello/api.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky <ferenj@effective-range.com>
2+
# SPDX-FileCopyrightText: 2024 Attila Gombos <attila.gombos@effective-range.com>
3+
# SPDX-License-Identifier: MIT
4+
15
from dataclasses import dataclass
26
from typing import Any
37

hello/discoverer.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky <ferenj@effective-range.com>
2+
# SPDX-FileCopyrightText: 2024 Attila Gombos <attila.gombos@effective-range.com>
3+
# SPDX-License-Identifier: MIT
4+
15
from dataclasses import dataclass
26
from enum import Enum
37
from typing import Any, Protocol
8+
from uuid import UUID
49

510
from common_utility import IReusableTimer
611
from context_logger import get_logger
@@ -36,7 +41,7 @@ def stop(self) -> None:
3641
def discover(self, query: ServiceQuery | None = None) -> None:
3742
raise NotImplementedError()
3843

39-
def get_services(self) -> dict[str, ServiceInfo]:
44+
def get_services(self) -> dict[UUID, ServiceInfo]:
4045
raise NotImplementedError()
4146

4247
def register(self, handler: OnDiscoveryEvent) -> None:
@@ -56,7 +61,7 @@ def __init__(self, sender: Sender, receiver: Receiver) -> None:
5661
self._receiver = receiver
5762
self._group: Group | None = None
5863
self._matcher: ServiceMatcher | None = None
59-
self._cache: dict[str, ServiceInfo] = {}
64+
self._cache: dict[UUID, ServiceInfo] = {}
6065
self._handlers: list[OnDiscoveryEvent] = []
6166

6267
def __enter__(self) -> Discoverer:
@@ -88,7 +93,7 @@ def discover(self, query: ServiceQuery | None = None) -> None:
8893
else:
8994
log.warning('Cannot discover services, discoverer not started', query=query)
9095

91-
def get_services(self) -> dict[str, ServiceInfo]:
96+
def get_services(self) -> dict[UUID, ServiceInfo]:
9297
return self._cache.copy()
9398

9499
def register(self, handler: OnDiscoveryEvent) -> None:
@@ -102,14 +107,14 @@ def get_handlers(self) -> list[OnDiscoveryEvent]:
102107

103108
def _handle_message(self, message: dict[str, Any]) -> None:
104109
try:
105-
service = ServiceInfo(**message)
110+
service = ServiceInfo(UUID(message['uuid']), message['name'], message['role'], message.get('urls', {}))
106111
self._handle_service(service)
107112
except Exception as error:
108113
log.warn('Failed to handle received message', data=message, error=error)
109114

110115
def _handle_service(self, service: ServiceInfo) -> None:
111116
if self._matcher and self._matcher.matches(service):
112-
cached = self._cache.get(service.name)
117+
cached = self._cache.get(service.uuid)
113118

114119
if event := self._create_event(cached, service):
115120
self._handle_event(event)
@@ -128,7 +133,7 @@ def _create_event(self, cached: ServiceInfo | None, service: ServiceInfo) -> Dis
128133

129134
def _handle_event(self, event: DiscoveryEvent) -> None:
130135
service = event.service
131-
self._cache[service.name] = service
136+
self._cache[service.uuid] = service
132137
for callback in self._handlers:
133138
try:
134139
callback(event)
@@ -158,7 +163,7 @@ def stop(self) -> None:
158163
def discover(self, query: ServiceQuery | None = None) -> None:
159164
self._discoverer.discover(query)
160165

161-
def get_services(self) -> dict[str, ServiceInfo]:
166+
def get_services(self) -> dict[UUID, ServiceInfo]:
162167
return self._discoverer.get_services()
163168

164169
def register(self, handler: OnDiscoveryEvent) -> None:

hello/group.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky <ferenj@effective-range.com>
2+
# SPDX-FileCopyrightText: 2024 Attila Gombos <attila.gombos@effective-range.com>
3+
# SPDX-License-Identifier: MIT
4+
15
from dataclasses import dataclass
26
from enum import Enum
37

hello/receiver.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky <ferenj@effective-range.com>
2+
# SPDX-FileCopyrightText: 2024 Attila Gombos <attila.gombos@effective-range.com>
3+
# SPDX-License-Identifier: MIT
4+
15
from concurrent.futures import ThreadPoolExecutor
26
from typing import Any, Protocol
37

hello/scheduler.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky <ferenj@effective-range.com>
2+
# SPDX-FileCopyrightText: 2024 Attila Gombos <attila.gombos@effective-range.com>
3+
# SPDX-License-Identifier: MIT
4+
15
from typing import TypeVar, Generic, Any
26

37
from common_utility import IReusableTimer

hello/sender.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky <ferenj@effective-range.com>
2+
# SPDX-FileCopyrightText: 2024 Attila Gombos <attila.gombos@effective-range.com>
3+
# SPDX-License-Identifier: MIT
4+
15
from typing import Any, cast
26

37
from context_logger import get_logger
@@ -65,6 +69,10 @@ def send(self, data: Any) -> None:
6569
def _convert_to_dict(self, data: Any) -> dict[str, Any] | None:
6670
if isinstance(data, dict):
6771
return data
72+
elif hasattr(data, 'to_dict') and callable(getattr(data, 'to_dict')):
73+
return cast(dict[str, Any], data.to_dict())
74+
elif hasattr(data, 'as_dict') and callable(getattr(data, 'as_dict')):
75+
return cast(dict[str, Any], data.as_dict())
6876
elif hasattr(data, '__dict__'):
6977
return cast(dict[str, Any], data.__dict__)
7078
return None

hello/service.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,31 @@
1+
# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky <ferenj@effective-range.com>
2+
# SPDX-FileCopyrightText: 2024 Attila Gombos <attila.gombos@effective-range.com>
3+
# SPDX-License-Identifier: MIT
4+
15
import re
26
from dataclasses import dataclass, field
7+
from typing import Any
8+
from uuid import UUID
39

410

511
@dataclass
612
class ServiceInfo:
13+
uuid: UUID
714
name: str
815
role: str
916
urls: dict[str, str] = field(default_factory=dict)
1017

18+
def __repr__(self) -> str:
19+
return f"ServiceInfo(uuid='{self.uuid}', name='{self.name}', role='{self.role}', urls='{self.urls}')"
20+
21+
def to_dict(self) -> dict[str, Any]:
22+
return {
23+
'uuid': str(self.uuid),
24+
'name': self.name,
25+
'role': self.role,
26+
'urls': self.urls
27+
}
28+
1129

1230
@dataclass
1331
class ServiceQuery(object):

tests/advertizerIntegrationTest.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import unittest
22
from unittest import TestCase
3+
from uuid import uuid4
34

45
from common_utility import ReusableTimer
56
from context_logger import setup_logging
@@ -10,18 +11,17 @@
1011
ServiceQuery, ScheduledAdvertizer
1112

1213
GROUP = Group('test-group', 'udp://239.0.0.1:5555')
14+
SERVICE_INFO = ServiceInfo(uuid4(), 'test-service', 'test-role', {'test': 'http://localhost:8080'})
1315

1416

1517
class AdvertizerIntegrationTest(TestCase):
16-
SERVICE_INFO = ServiceInfo('test-service', 'test-role', {'test': 'http://localhost:8080'})
1718

1819
@classmethod
1920
def setUpClass(cls):
2021
setup_logging('hello', 'DEBUG', warn_on_overwrite=False)
2122

2223
def setUp(self):
2324
print()
24-
self.SERVICE_INFO = ServiceInfo('test-service', 'test-role', {'test': 'http://localhost:8080'})
2525

2626
def test_sends_hello_when_advertises_service(self):
2727
# Given
@@ -35,12 +35,12 @@ def test_sends_hello_when_advertises_service(self):
3535
advertizer.start(GROUP)
3636

3737
# When
38-
advertizer.advertise(self.SERVICE_INFO)
38+
advertizer.advertise(SERVICE_INFO)
3939

4040
wait_for_assertion(0.1, lambda: self.assertEqual(1, len(messages)))
4141

4242
# Then
43-
self.assertEqual([self.SERVICE_INFO.__dict__], messages)
43+
self.assertEqual([SERVICE_INFO.to_dict()], messages)
4444

4545
def test_sends_hello_when_query_received(self):
4646
# Given
@@ -56,15 +56,15 @@ def test_sends_hello_when_query_received(self):
5656
test_receiver.start(GROUP.hello())
5757
test_receiver.register(lambda message: messages.append(message))
5858

59-
advertizer.start(GROUP, self.SERVICE_INFO)
59+
advertizer.start(GROUP, SERVICE_INFO)
6060

6161
# When
6262
test_sender.send(ServiceQuery('test-service', 'test-role'))
6363

6464
wait_for_assertion(0.1, lambda: self.assertEqual(1, len(messages)))
6565

6666
# Then
67-
self.assertEqual([self.SERVICE_INFO.__dict__], messages)
67+
self.assertEqual([SERVICE_INFO.to_dict()], messages)
6868

6969
def test_sends_hello_when_info_changed_and_query_received(self):
7070
# Given
@@ -81,15 +81,17 @@ def test_sends_hello_when_info_changed_and_query_received(self):
8181
test_receiver.register(lambda message: messages.append(message))
8282

8383
advertizer.start(GROUP)
84-
advertizer.advertise(self.SERVICE_INFO)
84+
advertizer.advertise(SERVICE_INFO)
8585

8686
query = ServiceQuery('test-service', 'test-role')
8787
test_sender.send(query)
8888

8989
wait_for_assertion(0.1, lambda: self.assertEqual(2, len(messages)))
9090

91-
self.SERVICE_INFO.urls['test'] = 'http://localhost:9090'
92-
advertizer.advertise(self.SERVICE_INFO)
91+
new_service_info = ServiceInfo(
92+
SERVICE_INFO.uuid, SERVICE_INFO.name, SERVICE_INFO.role, {'test': 'http://localhost:9090'}
93+
)
94+
advertizer.advertise(new_service_info)
9395

9496
# When
9597
test_sender.send(query)
@@ -98,10 +100,7 @@ def test_sends_hello_when_info_changed_and_query_received(self):
98100

99101
# Then
100102
self.assertEqual([
101-
{'name': 'test-service', 'role': 'test-role', 'urls': {'test': 'http://localhost:8080'}},
102-
{'name': 'test-service', 'role': 'test-role', 'urls': {'test': 'http://localhost:8080'}},
103-
{'name': 'test-service', 'role': 'test-role', 'urls': {'test': 'http://localhost:9090'}},
104-
{'name': 'test-service', 'role': 'test-role', 'urls': {'test': 'http://localhost:9090'}}
103+
SERVICE_INFO.to_dict(), SERVICE_INFO.to_dict(), new_service_info.to_dict(), new_service_info.to_dict()
105104
], messages)
106105

107106
def test_sends_hello_when_schedules_advertisement_once(self):
@@ -118,12 +117,12 @@ def test_sends_hello_when_schedules_advertisement_once(self):
118117
scheduled_advertizer.start(GROUP)
119118

120119
# When
121-
scheduled_advertizer.schedule(self.SERVICE_INFO, interval=0.01, one_shot=True)
120+
scheduled_advertizer.schedule(SERVICE_INFO, interval=0.01, one_shot=True)
122121

123122
wait_for_assertion(0.1, lambda: self.assertEqual(1, len(messages)))
124123

125124
# Then
126-
self.assertEqual([self.SERVICE_INFO.__dict__], messages)
125+
self.assertEqual([SERVICE_INFO.to_dict()], messages)
127126

128127
def test_sends_hello_when_schedules_advertisement_periodically(self):
129128
# Given
@@ -139,7 +138,7 @@ def test_sends_hello_when_schedules_advertisement_periodically(self):
139138
scheduled_advertizer.start(GROUP)
140139

141140
# When
142-
scheduled_advertizer.schedule(self.SERVICE_INFO, interval=0.01)
141+
scheduled_advertizer.schedule(SERVICE_INFO, interval=0.01)
143142

144143
# Then
145144
wait_for_assertion(0.1, lambda: self.assertEqual(5, len(messages)))

0 commit comments

Comments
 (0)