@@ -58,6 +140,11 @@
class="material-icons">open_in_new
{% endif %}
+
+ {% if physicalDevice.script is defined %}
+ install_desktop
+ {% endif %}
+ |
{% endfor %}
diff --git a/src/www/app/utils/api.py b/src/www/app/utils/api.py
index 4a04f112..b3c97524 100644
--- a/src/www/app/utils/api.py
+++ b/src/www/app/utils/api.py
@@ -1,4 +1,4 @@
-import json
+import json, os
from typing import List
import sys
from typing import List
@@ -9,7 +9,7 @@
from pdmodels.Models import PhysicalDevice, LogicalDevice, PhysicalToLogicalMapping, DeviceNote, Location
-end_point = 'http://restapi:5687'
+end_point = os.getenv('IOTA_API_URL', 'http://restapi:5687')
def get_sources(token: str) -> List[str]:
"""
@@ -256,6 +256,10 @@ def create_logical_device(physical_device: PhysicalDevice, token: str) ->str:
"location": physical_device.location,
}
+ # This is to work around a problem that turned up after the change to PostGIS.
+ if logicalJson['location'] is not None and (logicalJson['location'].lat is None or logicalJson['location'].long is None):
+ logicalJson.pop('location')
+
response = requests.post(f'{end_point}/broker/api/logical/devices/', json=logicalJson, headers=headers)
response.raise_for_status()
@@ -361,8 +365,8 @@ def change_user_password(password: str, token: str) -> str:
Params:
password: str - User's new password
token: str - User's authentication token
-
- reutrn:
+
+ return:
token: str - User's new authentication token
"""
headers = {"Authorization": f"Bearer {token}"}
diff --git a/test/python/test_dao.py b/test/python/test_dao.py
index 2844e496..1fa29f77 100644
--- a/test/python/test_dao.py
+++ b/test/python/test_dao.py
@@ -10,6 +10,17 @@
logger = logging.getLogger(__name__)
logging.captureWarnings(True)
+
+def _create_test_user() -> str:
+ test_uname=os.urandom(4).hex()
+ dao.user_add(uname=test_uname, passwd='password', disabled=False)
+ return test_uname
+
+
+def _now() -> datetime.datetime:
+ return datetime.datetime.now(tz=datetime.timezone.utc)
+
+
class TestDAO(unittest.TestCase):
def setUp(self):
@@ -22,7 +33,8 @@ def setUp(self):
truncate physical_logical_map cascade;
truncate device_notes cascade;
truncate physical_timeseries cascade;
- truncate raw_messages cascade''')
+ truncate raw_messages cascade;
+ delete from sources where source_name = 'axistech';''')
finally:
dao.free_conn(conn)
@@ -30,11 +42,25 @@ def test_get_all_physical_sources(self):
sources = dao.get_all_physical_sources()
self.assertEqual(sources, ['greenbrain', 'ict_eagleio', 'ttn', 'wombat', 'ydoc'])
- def now(self):
- return datetime.datetime.now(tz=datetime.timezone.utc)
+ def test_add_physical_source(self):
+ sources = dao.get_all_physical_sources()
+ self.assertFalse(BrokerConstants.AXISTECH in sources)
+ dao.add_physical_source(BrokerConstants.AXISTECH)
+ sources = dao.get_all_physical_sources()
+ self.assertTrue(BrokerConstants.AXISTECH in sources)
+
+ # Do it again to ensure it doesn't crash, and there is only one instance of the string.
+ dao.add_physical_source(BrokerConstants.AXISTECH)
+ sources = dao.get_all_physical_sources()
+ i = 0
+ for s in sources:
+ if s == BrokerConstants.AXISTECH:
+ i += 1
+
+ self.assertEqual(1, i)
def _create_physical_device(self, dev: PhysicalDevice = None) -> Tuple[PhysicalDevice, PhysicalDevice]:
- last_seen = self.now()
+ last_seen = _now()
if dev is None:
dev = PhysicalDevice(source_name='ttn', name='Test Device', location=Location(lat=3, long=-31), last_seen=last_seen,
source_ids={'appId': 'x', 'devId': 'y'},
@@ -42,11 +68,6 @@ def _create_physical_device(self, dev: PhysicalDevice = None) -> Tuple[PhysicalD
return (dev, dao.create_physical_device(dev))
- def _create_test_user(self) -> str:
- test_uname=os.urandom(4).hex()
- dao.user_add(uname=test_uname, passwd='password', disabled=False)
- return test_uname
-
def test_create_physical_device(self):
dev, new_dev = self._create_physical_device()
@@ -203,7 +224,7 @@ def test_delete_physical_device_note(self):
def _create_default_logical_device(self, dev=None) -> Tuple[LogicalDevice, LogicalDevice]:
if dev is None:
- last_seen = self.now()
+ last_seen = _now()
dev = LogicalDevice(name='Test Device', location=Location(lat=3, long=-31), last_seen=last_seen,
properties={'appId': 'x', 'devId': 'y', 'other': 'z'})
@@ -257,28 +278,28 @@ def test_delete_logical_device(self):
def test_insert_mapping(self):
pdev, new_pdev = self._create_physical_device()
ldev, new_ldev = self._create_default_logical_device()
- mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=self.now())
+ mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=_now())
# This should work.
dao.insert_mapping(mapping)
# This should fail due to duplicate start time.
- self.assertRaises(dao.DAOException, dao.insert_mapping, mapping)
+ self.assertRaises(ValueError, dao.insert_mapping, mapping)
# This should fail due to the physical device is still mapped to something.
time.sleep(0.001)
- mapping.start_time=self.now()
- self.assertRaises(dao.DAOException, dao.insert_mapping, mapping)
+ mapping.start_time= _now()
+ self.assertRaises(ValueError, dao.insert_mapping, mapping)
# Unmap the physical device so the next test doesn't fail due to the device being mapped.
dao.end_mapping(pd=new_pdev)
# The insert_mapping operation should succeed because the timestamp is different from above.
- mapping.start_time=self.now()
+ mapping.start_time= _now()
dao.insert_mapping(mapping)
pdx = copy.deepcopy(new_pdev)
pdx.uid = -1
- mapping = PhysicalToLogicalMapping(pd=pdx, ld=new_ldev, start_time=self.now())
+ mapping = PhysicalToLogicalMapping(pd=pdx, ld=new_ldev, start_time=_now())
# This should fail due to invalid physical uid.
self.assertRaises(dao.DAODeviceNotFound, dao.insert_mapping, mapping)
@@ -286,7 +307,7 @@ def test_insert_mapping(self):
dao.end_mapping(pd=new_pdev)
ldx = copy.deepcopy(new_ldev)
ldx.uid = -1
- mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=ldx, start_time=self.now())
+ mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=ldx, start_time=_now())
# This should fail due to invalid logical uid.
self.assertRaises(dao.DAODeviceNotFound, dao.insert_mapping, mapping)
@@ -302,14 +323,14 @@ def test_get_current_device_mapping(self):
self.assertIsNone(dao.get_current_device_mapping(ld=new_ldev))
# Confirm pd or ld must be given.
- self.assertRaises(dao.DAOException, dao.get_current_device_mapping)
+ self.assertRaises(ValueError, dao.get_current_device_mapping)
# Confirm only pd or ld can be given.
- self.assertRaises(dao.DAOException, dao.get_current_device_mapping, -1, -1)
- self.assertRaises(dao.DAOException, dao.get_current_device_mapping, new_pdev, new_ldev)
+ self.assertRaises(ValueError, dao.get_current_device_mapping, -1, -1)
+ self.assertRaises(ValueError, dao.get_current_device_mapping, new_pdev, new_ldev)
# confirm a physical device can be mapped to a logical device.
- mapping1 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=self.now())
+ mapping1 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=_now())
dao.insert_mapping(mapping1)
self.assertEqual(mapping1, dao.get_current_device_mapping(pd=new_pdev.uid))
@@ -322,7 +343,7 @@ def test_get_current_device_mapping(self):
time.sleep(0.001)
pdev2, new_pdev2 = self._create_physical_device()
- mapping2 = PhysicalToLogicalMapping(pd=new_pdev2, ld=new_ldev, start_time=self.now())
+ mapping2 = PhysicalToLogicalMapping(pd=new_pdev2, ld=new_ldev, start_time=_now())
dao.insert_mapping(mapping2)
# This test must fail if multiple mappings are found, because there should not be
@@ -341,7 +362,7 @@ def test_get_latest_device_mapping(self):
ldev, new_ldev = self._create_default_logical_device()
# confirm getting the latest mapping returns a current mapping
- mapping1 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=self.now())
+ mapping1 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=_now())
dao.insert_mapping(mapping1)
self.assertEqual(mapping1, dao.get_current_device_mapping(pd=new_pdev.uid, only_current_mapping=True))
@@ -367,7 +388,7 @@ def test_get_latest_device_mapping(self):
self.assertTrue(self.compare_mappings_ignore_end_time(mapping1, mapping2))
time.sleep(0.1)
- mapping1.start_time = self.now()
+ mapping1.start_time = _now()
dao.insert_mapping(mapping1)
# with a new mapping with no end time, both calls should again return the same thing.
@@ -381,7 +402,14 @@ def test_end_mapping(self):
pdev, new_pdev = self._create_physical_device()
ldev, new_ldev = self._create_default_logical_device()
- mapping1 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=self.now())
+ # Confirm pd or ld must be given.
+ self.assertRaises(ValueError, dao.end_mapping)
+
+ # Confirm only pd or ld can be given.
+ self.assertRaises(ValueError, dao.end_mapping, -1, -1)
+ self.assertRaises(ValueError, dao.end_mapping, new_pdev, new_ldev)
+
+ mapping1 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=_now())
dao.insert_mapping(mapping1)
mappings = dao.get_unmapped_physical_devices()
@@ -394,7 +422,7 @@ def test_end_mapping(self):
pdev2 = copy.deepcopy(pdev)
pdev2.name = 'D2'
pdev2, new_pdev2 = self._create_physical_device(dev=pdev2)
- mapping2 = PhysicalToLogicalMapping(pd=new_pdev2, ld=new_ldev, start_time=self.now())
+ mapping2 = PhysicalToLogicalMapping(pd=new_pdev2, ld=new_ldev, start_time=_now())
dao.insert_mapping(mapping2)
mappings = dao.get_unmapped_physical_devices()
@@ -406,13 +434,13 @@ def test_end_mapping(self):
# Avoid a unique key constraint due to identical timestamps.
time.sleep(0.001)
- mapping1.start_time = self.now()
+ mapping1.start_time = _now()
dao.insert_mapping(mapping1)
dao.end_mapping(pd=new_pdev)
mappings = dao.get_unmapped_physical_devices()
self.assertEqual(len(mappings), 2)
- mapping2.start_time = self.now()
+ mapping2.start_time = _now()
dao.insert_mapping(mapping2)
mappings = dao.get_unmapped_physical_devices()
self.assertEqual(len(mappings), 1)
@@ -427,19 +455,19 @@ def compare_mappings_ignore_end_time(self, m1: PhysicalToLogicalMapping, m2: Phy
def test_get_mappings(self):
pdev, new_pdev = self._create_physical_device()
ldev, new_ldev = self._create_default_logical_device()
- mapping1 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=self.now())
+ mapping1 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=_now())
dao.insert_mapping(mapping1)
pdev2 = copy.deepcopy(pdev)
pdev2.name = 'D2'
pdev2, new_pdev2 = self._create_physical_device(dev=pdev2)
time.sleep(0.1)
- mapping2 = PhysicalToLogicalMapping(pd=new_pdev2, ld=new_ldev, start_time=self.now())
+ mapping2 = PhysicalToLogicalMapping(pd=new_pdev2, ld=new_ldev, start_time=_now())
dao.insert_mapping(mapping2)
# Avoid a unique key constraint due to identical timestamps.
time.sleep(0.1)
- mapping3 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=self.now())
+ mapping3 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=_now())
dao.insert_mapping(mapping3)
mappings = dao.get_logical_device_mappings(new_ldev)
@@ -453,7 +481,7 @@ def test_get_all_logical_device_mappings(self):
pdev, new_pdev = self._create_physical_device()
ldev, new_ldev = self._create_default_logical_device()
- mapping1 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=self.now())
+ mapping1 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=_now())
dao.insert_mapping(mapping1)
time.sleep(0.1)
dao.end_mapping(ld=new_ldev.uid)
@@ -463,7 +491,7 @@ def test_get_all_logical_device_mappings(self):
pdev2.name = 'D2'
pdev2, new_pdev2 = self._create_physical_device(dev=pdev2)
time.sleep(0.1)
- mapping2 = PhysicalToLogicalMapping(pd=new_pdev2, ld=new_ldev, start_time=self.now())
+ mapping2 = PhysicalToLogicalMapping(pd=new_pdev2, ld=new_ldev, start_time=_now())
dao.insert_mapping(mapping2)
time.sleep(0.1)
dao.end_mapping(ld=new_ldev.uid)
@@ -473,7 +501,7 @@ def test_get_all_logical_device_mappings(self):
pdev3.name = 'D3'
pdev3, new_pdev3 = self._create_physical_device(dev=pdev3)
time.sleep(0.1)
- mapping3 = PhysicalToLogicalMapping(pd=new_pdev3, ld=new_ldev, start_time=self.now())
+ mapping3 = PhysicalToLogicalMapping(pd=new_pdev3, ld=new_ldev, start_time=_now())
dao.insert_mapping(mapping3)
mappings = dao.get_logical_device_mappings(ld=new_ldev.uid)
@@ -487,7 +515,7 @@ def test_get_unmapped_devices(self):
ldev, new_ldev = self._create_default_logical_device()
# confirm a physical device can be mapped to a logical device.
- mapping1 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=self.now())
+ mapping1 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=_now())
dao.insert_mapping(mapping1)
pdev2 = copy.deepcopy(pdev)
@@ -510,15 +538,15 @@ def test_get_unmapped_devices(self):
self.assertTrue(new_pdev3 in unmapped_devs)
self.assertTrue(new_pdev4 in unmapped_devs)
- mapping2 = PhysicalToLogicalMapping(pd=new_pdev2, ld=new_ldev, start_time=self.now())
+ mapping2 = PhysicalToLogicalMapping(pd=new_pdev2, ld=new_ldev, start_time=_now())
dao.insert_mapping(mapping2)
ldev2 = copy.deepcopy(ldev)
ldev2.name = 'L2'
- ldev2.last_seen = self.now()
+ ldev2.last_seen = _now()
ldev2, new_ldev2 = self._create_default_logical_device(dev=ldev2)
- mapping3 = PhysicalToLogicalMapping(pd=new_pdev4, ld=new_ldev2, start_time=self.now())
+ mapping3 = PhysicalToLogicalMapping(pd=new_pdev4, ld=new_ldev2, start_time=_now())
dao.insert_mapping(mapping3)
unmapped_devs = dao.get_unmapped_physical_devices()
self.assertEqual(len(unmapped_devs), 2)
@@ -527,10 +555,10 @@ def test_get_unmapped_devices(self):
ldev3 = copy.deepcopy(ldev)
ldev3.name = 'L3'
- ldev3.last_seen = self.now()
+ ldev3.last_seen = _now()
ldev3, new_ldev3 = self._create_default_logical_device(dev=ldev3)
- mapping4 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev3, start_time=self.now())
+ mapping4 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev3, start_time=_now())
dao.insert_mapping(mapping4)
unmapped_devs = dao.get_unmapped_physical_devices()
self.assertEqual(len(unmapped_devs), 1)
@@ -539,12 +567,12 @@ def test_get_unmapped_devices(self):
def test_add_raw_json_message(self):
uuid1 = uuid.uuid4()
- obj1 = {'a':1, 'b':'2', 'c':True, 'd':False}
- dao.add_raw_json_message('ttn', self.now(), uuid1, obj1)
+ obj1 = {'a': 1, 'b': '2', 'c': True, 'd': False}
+ dao.add_raw_json_message('ttn', _now(), uuid1, obj1)
uuid2 = uuid.uuid4()
- obj2 = {'a':1, 'b':'2', 'c':False, 'd':True}
- dao.add_raw_json_message('ttn', self.now(), uuid2, obj2, 1)
+ obj2 = {'a': 1, 'b': '2', 'c': False, 'd': True}
+ dao.add_raw_json_message('ttn', _now(), uuid2, obj2, 1)
with dao._get_connection() as conn, conn.cursor() as cursor:
cursor.execute('select physical_uid, correlation_id, json_msg from raw_messages order by uid asc')
@@ -563,23 +591,24 @@ def test_add_raw_json_message(self):
# Confirm the DAO raises a warning when trying to add a message with a
# duplicate UUID, but doesn't throw an exception.
with self.assertWarns(UserWarning):
- dao.add_raw_json_message('ttn', self.now(), uuid1, obj1)
+ dao.add_raw_json_message('ttn', _now(), uuid1, obj1)
def test_insert_physical_timeseries_message(self):
dev, new_dev = self._create_physical_device()
msg = {
- "p_uid":new_dev.uid,
- "timestamp":"2023-02-20T07:57:52Z",
- "timeseries":[
+ "p_uid": new_dev.uid,
+ "timestamp": "2023-02-20T07:57:52Z",
+ "timeseries": [
{
- "name":"airTemperature",
- "value":35.1
+ "name": "x",
+ "value": 35.1
}
],
- "broker_correlation_id":"3d7762f6-bcc6-44d4-82ba-49b07e61e601"
+ "broker_correlation_id": "3d7762f6-bcc6-44d4-82ba-49b07e61e601"
}
+
msg_ts = dateutil.parser.isoparse(msg[BrokerConstants.TIMESTAMP_KEY])
dao.insert_physical_timeseries_message(msg)
@@ -590,15 +619,95 @@ def test_insert_physical_timeseries_message(self):
self.assertEqual(ts, msg_ts)
self.assertEqual(msg, retrieved_msg)
+ def test_get_physical_timeseries_messages(self):
+ _, new_pdev = self._create_physical_device()
+
+ # Basic smoke test - no messages, no results.
+ msgs = dao.get_physical_timeseries_message(None, None, 1, only_timestamp=True, p_uid=new_pdev.uid)
+ self.assertSequenceEqual(msgs, [])
+
+ msgs = dao.get_physical_timeseries_message(None, None, 1, only_timestamp=True, l_uid=20)
+ self.assertSequenceEqual(msgs, [])
+
+ msg_list = [
+ {BrokerConstants.PHYSICAL_DEVICE_UID_KEY: new_pdev.uid, BrokerConstants.TIMESTAMP_KEY: "2023-02-20T01:00+11:00"},
+ {BrokerConstants.PHYSICAL_DEVICE_UID_KEY: new_pdev.uid, BrokerConstants.TIMESTAMP_KEY: "2023-02-20T00:30+11:00"},
+ {BrokerConstants.PHYSICAL_DEVICE_UID_KEY: new_pdev.uid, BrokerConstants.TIMESTAMP_KEY: "2023-02-20T00:00+11:00"},
+ {BrokerConstants.PHYSICAL_DEVICE_UID_KEY: new_pdev.uid, BrokerConstants.TIMESTAMP_KEY: "2023-02-19T23:30+11:00"},
+ {BrokerConstants.PHYSICAL_DEVICE_UID_KEY: new_pdev.uid, BrokerConstants.TIMESTAMP_KEY: "2023-02-19T23:00+11:00"},
+ {BrokerConstants.PHYSICAL_DEVICE_UID_KEY: new_pdev.uid, BrokerConstants.TIMESTAMP_KEY: "2023-02-19T22:30+11:00"}
+ ]
+
+ msg_ts = []
+ for msg in msg_list:
+ dao.insert_physical_timeseries_message(msg)
+ msg_ts.append(dateutil.parser.isoparse(msg[BrokerConstants.TIMESTAMP_KEY]))
+
+ msgs = dao.get_physical_timeseries_message(None, None, 1, only_timestamp=False, include_received_at=False, p_uid=new_pdev.uid)
+ self.assertEqual(len(msgs), 1)
+ self.assertEqual(dateutil.parser.isoparse(msgs[0][BrokerConstants.TIMESTAMP_KEY]), msg_ts[0])
+
+ msgs = dao.get_physical_timeseries_message(None, None, None, only_timestamp=True, p_uid=new_pdev.uid)
+
+ self.assertEqual(len(msgs), len(msg_list))
+ for i, msg in enumerate(msgs):
+ print(msg)
+ self.assertEqual(dateutil.parser.isoparse(msg[BrokerConstants.TIMESTAMP_KEY]), msg_ts[i])
+
+ _, new_ldev = self._create_default_logical_device()
+ mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=_now())
+ dao.insert_mapping(mapping)
+
+ # Ensure 1 message will be returned from the DAO when no start or end is given.
+ now = _now() - datetime.timedelta(minutes=1)
+ td_30_mins = datetime.timedelta(minutes=30)
+
+ for i, msg in enumerate(msg_list):
+ msg[BrokerConstants.TIMESTAMP_KEY] = (now + (i * td_30_mins)).isoformat()
+ msg[BrokerConstants.LOGICAL_DEVICE_UID_KEY] = new_ldev.uid
+
+ msg_list.sort(key=lambda m: m[BrokerConstants.TIMESTAMP_KEY], reverse=True)
+
+ msg_ts.clear()
+ for msg in msg_list:
+ dao.insert_physical_timeseries_message(msg)
+ msg_ts.append(dateutil.parser.isoparse(msg[BrokerConstants.TIMESTAMP_KEY]))
+
+ # This will return a single message because 'end' is None, meaning the DAO will set the
+ # end timestamp to 'now'. This batch of messages has timestamps from 1 minute ago to a
+ # couple of hours in the future, so only the message with the earliest timestamp in the
+ # batch should be returned.
+ msgs = dao.get_physical_timeseries_message(only_timestamp=True, l_uid=new_ldev.uid)
+ self.assertEqual(len(msgs), 1)
+ self.assertEqual(dateutil.parser.isoparse(msgs[0][BrokerConstants.TIMESTAMP_KEY]), msg_ts[-1])
+
+ # This will return all the messages because 'end' has been set past the latest message timestamp.
+ msgs = dao.get_physical_timeseries_message(end=now + datetime.timedelta(days=1), only_timestamp=True, l_uid=new_ldev.uid)
+ self.assertEqual(len(msgs), len(msg_list))
+ for i, msg in enumerate(msgs):
+ self.assertEqual(dateutil.parser.isoparse(msg[BrokerConstants.TIMESTAMP_KEY]), msg_ts[i])
+
+ # Should return only the latest message.
+ msgs = dao.get_physical_timeseries_message(end=now + datetime.timedelta(days=1), only_timestamp=True, count=1, l_uid=new_ldev.uid)
+ self.assertEqual(len(msgs), 1)
+ self.assertEqual(dateutil.parser.isoparse(msgs[0][BrokerConstants.TIMESTAMP_KEY]), msg_ts[0])
+
+ self.assertRaises(ValueError, dao.get_physical_timeseries_message)
+ self.assertRaises(TypeError, dao.get_physical_timeseries_message, p_uid='x')
+ self.assertRaises(TypeError, dao.get_physical_timeseries_message, l_uid='x')
+ self.assertRaises(TypeError, dao.get_physical_timeseries_message, start='x', l_uid=1)
+ self.assertRaises(TypeError, dao.get_physical_timeseries_message, end='x', l_uid=1)
+ self.assertRaises(TypeError, dao.get_physical_timeseries_message, count='x', l_uid=1)
+
def test_add_raw_text_message(self):
uuid1 = uuid.uuid4()
msg1 = 'This is a text message.'
- dao.add_raw_text_message('greenbrain', self.now(), uuid1, msg1)
+ dao.add_raw_text_message('greenbrain', _now(), uuid1, msg1)
uuid2 = uuid.uuid4()
msg2 = 'This is a text message 2.'
- dao.add_raw_text_message('greenbrain', self.now(), uuid2, msg2, 2)
+ dao.add_raw_text_message('greenbrain', _now(), uuid2, msg2, 2)
with dao._get_connection() as conn, conn.cursor() as cursor:
cursor.execute('select physical_uid, correlation_id, text_msg from raw_messages')
self.assertEqual(2, cursor.rowcount)
@@ -616,21 +725,21 @@ def test_add_raw_text_message(self):
# Confirm the DAO raises a warning when trying to add a message with a
# duplicate UUID, but doesn't throw an exception.
with self.assertWarns(UserWarning):
- dao.add_raw_text_message('ttn', self.now(), uuid1, msg1)
+ dao.add_raw_text_message('ttn', _now(), uuid1, msg1)
def test_user_add(self):
- uname=self._create_test_user()
- users=dao.user_ls()
+ uname = _create_test_user()
+ users = dao.user_ls()
self.assertEqual(uname, users[-1])
def test_user_rm(self):
- uname=self._create_test_user()
+ uname = _create_test_user()
dao.user_rm(uname)
self.assertFalse(uname in dao.user_ls())
def test_user_set_read_only(self):
- uname=self._create_test_user()
+ uname= _create_test_user()
dao.user_set_read_only(uname, False)
user_token=dao.user_get_token(username=uname, password='password')
user=dao.get_user(auth_token=user_token)
@@ -638,39 +747,39 @@ def test_user_set_read_only(self):
def test_add_non_unique_user(self):
#Check that two users with the same username cannot be created
- uname=self._create_test_user()
+ uname = _create_test_user()
self.assertRaises(dao.DAOUniqeConstraintException, dao.user_add, uname, 'password', False)
def test_get_user_token(self):
- uname=self._create_test_user()
+ uname = _create_test_user()
self.assertIsNotNone(dao.user_get_token(username=uname, password='password'))
self.assertIsNone(dao.user_get_token(username=uname, password='x'))
def test_user_token_refresh(self):
- uname=self._create_test_user()
- token1=dao.user_get_token(username=uname, password='password')
+ uname = _create_test_user()
+ token1 = dao.user_get_token(username=uname, password='password')
dao.token_refresh(uname=uname)
- token2=dao.user_get_token(username=uname, password='password')
+ token2 = dao.user_get_token(username=uname, password='password')
self.assertNotEqual(token1, token2)
def test_user_token_disable(self):
- uname=self._create_test_user()
- user_token=dao.user_get_token(username=uname, password='password')
+ uname = _create_test_user()
+ user_token = dao.user_get_token(username=uname, password='password')
dao.token_disable(uname)
self.assertFalse(dao.token_is_valid(user_token))
def test_user_token_enable(self):
- uname=self._create_test_user()
- user_token=dao.user_get_token(username=uname, password='password')
+ uname = _create_test_user()
+ user_token = dao.user_get_token(username=uname, password='password')
dao.token_disable(uname)
dao.token_enable(uname)
self.assertTrue(dao.token_is_valid(user_token))
def test_user_change_password(self):
- uname=self._create_test_user()
+ uname = _create_test_user()
dao.user_change_password(uname, 'nuiscyeriygsreiuliu')
self.assertIsNotNone(dao.user_get_token(username=uname, password='nuiscyeriygsreiuliu'))
diff --git a/test/python/test_get_physical_messages.py b/test/python/test_get_physical_messages.py
index 6f76e2ab..8e54ccfd 100644
--- a/test/python/test_get_physical_messages.py
+++ b/test/python/test_get_physical_messages.py
@@ -6,12 +6,16 @@
import pytest
from fastapi.testclient import TestClient
+import BrokerConstants
import test_utils as tu
import api.client.DAO as dao
from pdmodels.Models import PhysicalDevice
from restapi.RestAPI import app
-ts: dt.datetime = tu.now() - dt.timedelta(days=683.0)
+ts: dt.datetime = dateutil.parser.isoparse('2024-05-10T23:00:00Z')
+latest_ts = ts
+earliest_ts = ts
+
interval = dt.timedelta(minutes=15.0)
timestamps = []
@@ -31,7 +35,7 @@ def create_user():
@pytest.fixture(scope='module')
def create_msgs():
- global interval, msgs, pd, timestamps, ts
+ global interval, msgs, pd, timestamps, ts, earliest_ts
logging.info('Generating messages')
@@ -40,11 +44,12 @@ def create_msgs():
with open('/tmp/msgs.json', 'w') as f:
for i in range(0, max_msgs + 1):
timestamps.append(ts)
- msg = {'ts': ts.isoformat(), 'i': i}
+ msg = {BrokerConstants.TIMESTAMP_KEY: ts.isoformat(), 'i': i}
msgs.append(msg)
s = f'{pd.uid}\t{ts.isoformat()}\t{json.dumps(msg)}'
print(s, file=f)
- ts = ts + interval
+ ts = ts - interval
+ earliest_ts = ts
with open('/tmp/msgs.json', 'r') as f:
with dao.get_connection() as conn, conn.cursor() as cursor:
@@ -64,107 +69,109 @@ def test_client(create_user, create_msgs):
def test_no_params_no_msgs(test_client):
- no_msg_pd: PhysicalDevice = dao.create_physical_device(PhysicalDevice(source_name='wombat', name='dummy', source_ids={'x': 1}))
- response = test_client.get(f'/broker/api/physical/messages/{no_msg_pd.uid}')
- assert response.status_code == 200
- assert response.json() == []
+ response = test_client.get(f'/broker/api/messages/')
+ assert response.status_code == 404
def test_no_params(test_client):
# Confirm the default count parameter value is correct, so 65536 of the 65537 messages are returned.
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}')
+ response = test_client.get(f'/broker/api/messages', params={'p_uid': pd.uid})
assert response.status_code == 200
- assert response.json() == msgs[:-1]
+ for a, b in zip(response.json(), msgs[:-1]):
+ assert a == b
def test_no_params_ts(test_client):
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}', params={'only_timestamp': 1})
+ response = test_client.get(f'/broker/api/messages', params={'p_uid': pd.uid, 'only_timestamp': 1})
assert response.status_code == 200
+
for a, b in zip(response.json(), timestamps):
if a is None:
break
- assert dateutil.parser.isoparse(a) == b
+ assert dateutil.parser.isoparse(a[BrokerConstants.TIMESTAMP_KEY]) == b
def test_count(test_client):
# Confirm the count parameter works.
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}', params={'count': 50})
+ response = test_client.get(f'/broker/api/messages/', params={'p_uid': pd.uid, 'count': 50})
assert response.status_code == 200
- assert response.json() == msgs[:50]
+ #assert response.json() == msgs[:50]
+ for a, b in zip(response.json(), msgs[:50]):
+ assert a == b
def test_count_ts(test_client):
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}', params={'count': 50, 'only_timestamp': 1})
+ response = test_client.get(f'/broker/api/messages', params={'p_uid': pd.uid, 'count': 50, 'only_timestamp': 1})
assert response.status_code == 200
for a, b in zip(response.json(), timestamps):
if a is None:
break
- assert dateutil.parser.isoparse(a) == b
+ assert dateutil.parser.isoparse(a[BrokerConstants.TIMESTAMP_KEY]) == b
def test_start_after_end(test_client):
- start_ts = ts + interval
-
# Confirm no messages returned when a start timestamp at or after the last message is used.
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}', params={'start': start_ts})
+ response = test_client.get(f'/broker/api/messages', params={'p_uid': pd.uid, 'start': latest_ts})
assert response.status_code == 200
assert response.json() == []
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}', params={'start': timestamps[max_msgs]})
+ response = test_client.get(f'/broker/api/messages', params={'p_uid': pd.uid, 'start': timestamps[0]})
assert response.status_code == 200
assert response.json() == []
def test_start_gives_gt(test_client):
# Confirm start time parameter gets the next message greater than, not greater than equal to.
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}', params={'start': timestamps[max_msgs - 1]})
+ response = test_client.get(f'/broker/api/messages', params={'p_uid': pd.uid, 'start': timestamps[1]})
assert response.status_code == 200
- assert response.json() == [msgs[max_msgs]]
+ assert response.json()[0] == msgs[0]
def test_invalid_count(test_client):
# Check the invalid count values.
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}', params={'count': -1})
+ response = test_client.get(f'/broker/api/messages', params={'p_uid': pd.uid, 'count': -1})
assert response.status_code == 422
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}', params={'count': 0})
+ response = test_client.get(f'/broker/api/messages', params={'p_uid': pd.uid, 'count': 0})
assert response.status_code == 422
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}', params={'count': 65537})
+ response = test_client.get(f'/broker/api/messages', params={'p_uid': pd.uid, 'count': 65537})
assert response.status_code == 422
def test_end(test_client):
# Test the end parameter
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}', params={'end': timestamps[0] - interval})
+ response = test_client.get(f'/broker/api/messages', params={'p_uid': pd.uid, 'end': earliest_ts})
assert response.status_code == 200
assert response.json() == []
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}', params={'end': timestamps[0]})
+ response = test_client.get(f'/broker/api/messages', params={'p_uid': pd.uid, 'end': timestamps[-1]})
assert response.status_code == 200
- assert response.json() == [msgs[0]]
+ assert response.json()[0] == msgs[-1]
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}', params={'end': timestamps[9]})
+ response = test_client.get(f'/broker/api/messages', params={'p_uid': pd.uid, 'end': timestamps[-9]})
assert response.status_code == 200
- assert response.json() == msgs[:10]
+ for a, b in zip(response.json(), msgs[-9:]):
+ assert a == b
def test_start_end(test_client):
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}', params={'start': timestamps[5], 'end': timestamps[9]})
+ response = test_client.get(f'/broker/api/messages/', params={'p_uid': pd.uid, 'start': timestamps[9], 'end': timestamps[5]})
assert response.status_code == 200
- assert response.json() == msgs[6:10]
+ for a, b in zip(response.json(), msgs[5:9]):
+ assert a == b
def test_invalid_start_end(test_client):
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}', params={'start': timestamps[5], 'end': timestamps[5]})
+ response = test_client.get(f'/broker/api/messages/', params={'p_uid': pd.uid, 'start': timestamps[5], 'end': timestamps[5]})
assert response.status_code == 422
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}', params={'start': timestamps[5], 'end': timestamps[4]})
+ response = test_client.get(f'/broker/api/messages/', params={'p_uid': pd.uid, 'start': timestamps[4], 'end': timestamps[5]})
assert response.status_code == 422
def test_invalid_count_end(test_client):
- response = test_client.get(f'/broker/api/physical/messages/{pd.uid}', params={'count': 1, 'end': timestamps[4]})
+ response = test_client.get(f'/broker/api/messages/', params={'p_uid': pd.uid, 'count': 1, 'end': timestamps[4]})
assert response.status_code == 422
diff --git a/test/python/test_restapi.py b/test/python/test_restapi.py
index 2ad80647..594b3652 100644
--- a/test/python/test_restapi.py
+++ b/test/python/test_restapi.py
@@ -1,16 +1,16 @@
import base64
-import copy, datetime, logging, time, unittest, uuid
-from typing_extensions import assert_type
-import re
+import copy
+import logging
+import os
+import time
+import unittest
from typing import Tuple
-import api.client.DAO as dao
import requests
+import api.client.DAO as dao
from pdmodels.Models import DeviceNote, PhysicalDevice, PhysicalToLogicalMapping, Location, LogicalDevice
-from typing import Tuple
-
-import os
+from test_utils import now
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(name)s: %(message)s', datefmt='%Y-%m-%dT%H:%M:%S%z')
logger = logging.getLogger(__name__)
@@ -62,13 +62,10 @@ def setUp(self):
self._admin_token = dao.user_get_token(self._admin_username, 'password')
self._ADMIN_HEADERS['Authorization'] = f'Bearer {self._admin_token}'
- def now(self):
- return datetime.datetime.now(tz=datetime.timezone.utc)
-
def _create_physical_device(self, expected_code=201, req_header=_HEADERS, dev=None) -> Tuple[
PhysicalDevice, PhysicalDevice]:
if dev is None:
- last_seen = self.now()
+ last_seen = now()
dev = PhysicalDevice(source_name='ttn', name='Test Device', location=Location(lat=3, long=-31),
last_seen=last_seen,
source_ids={'appId': 'x', 'devId': 'y'},
@@ -292,10 +289,12 @@ def test_get_device_notes(self):
LOGICAL DEVICES
--------------------------------------------------------------------------"""
- def _create_default_logical_device(self, expected_code=201, req_header=_HEADERS, dev=None) -> Tuple[
- LogicalDevice, LogicalDevice]:
+ def _create_default_logical_device(self, expected_code=201, req_header=None, dev=None) -> Tuple[LogicalDevice, LogicalDevice]:
+ if req_header is None:
+ req_header = self._HEADERS
+
if dev is None:
- last_seen = self.now()
+ last_seen = now()
dev = LogicalDevice(name='Test Device', location=Location(lat=3, long=-31), last_seen=last_seen,
properties={'appId': 'x', 'devId': 'y', 'other': 'z'})
@@ -416,7 +415,7 @@ def test_delete_logical_device(self):
def test_insert_mapping(self):
pdev, new_pdev = self._create_physical_device(req_header=self._ADMIN_HEADERS)
ldev, new_ldev = self._create_default_logical_device(req_header=self._ADMIN_HEADERS)
- mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=self.now())
+ mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=now())
# This should work.
url = f'{_BASE}/mappings/'
@@ -430,7 +429,7 @@ def test_insert_mapping(self):
# This should fail because the physical device has a current mapping.
time.sleep(0.001)
- mapping.start_time = self.now()
+ mapping.start_time = now()
payload = mapping.json()
r = requests.post(url, headers=self._ADMIN_HEADERS, data=payload)
self.assertEqual(r.status_code, 400)
@@ -438,14 +437,14 @@ def test_insert_mapping(self):
# End the current mapping and create a new one. This should work and
# simulates 'pausing' a physical device while working on it.
requests.patch(f'{url}physical/end/{mapping.pd.uid}', headers=self._ADMIN_HEADERS)
- mapping.start_time = self.now()
+ mapping.start_time = now()
payload = mapping.json()
r = requests.post(url, headers=self._ADMIN_HEADERS, data=payload)
self.assertEqual(r.status_code, 201)
pdx = copy.deepcopy(new_pdev)
pdx.uid = -1
- mapping = PhysicalToLogicalMapping(pd=pdx, ld=new_ldev, start_time=self.now())
+ mapping = PhysicalToLogicalMapping(pd=pdx, ld=new_ldev, start_time=now())
# This should fail due to invalid physical uid.
payload = mapping.json()
r = requests.post(url, headers=self._ADMIN_HEADERS, data=payload)
@@ -453,7 +452,7 @@ def test_insert_mapping(self):
ldx = copy.deepcopy(new_ldev)
ldx.uid = -1
- mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=ldx, start_time=self.now())
+ mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=ldx, start_time=now())
# End the current mapping for the phyiscal device so the RESTAPI doesn't
# return status 400.
@@ -467,7 +466,7 @@ def test_insert_mapping(self):
def test_get_mapping_from_physical(self):
pdev, new_pdev = self._create_physical_device(req_header=self._ADMIN_HEADERS)
ldev, new_ldev = self._create_default_logical_device(req_header=self._ADMIN_HEADERS)
- mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=self.now())
+ mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=now())
url = f'{_BASE}/mappings/'
payload = mapping.json()
@@ -491,7 +490,7 @@ def test_get_mapping_from_physical(self):
# Confirm the latest mapping is returned.
time.sleep(0.001)
mapping2 = copy.deepcopy(mapping)
- mapping2.start_time = self.now()
+ mapping2.start_time = now()
self.assertNotEqual(mapping, mapping2)
payload = mapping2.json()
r = requests.post(url, headers=self._ADMIN_HEADERS, data=payload)
@@ -505,7 +504,7 @@ def test_get_mapping_from_physical(self):
def test_get_mapping_from_logical(self):
pdev, new_pdev = self._create_physical_device(req_header=self._ADMIN_HEADERS)
ldev, new_ldev = self._create_default_logical_device(req_header=self._ADMIN_HEADERS)
- mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=self.now())
+ mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=now())
url = f'{_BASE}/mappings/'
payload = mapping.json()
@@ -529,7 +528,7 @@ def test_get_mapping_from_logical(self):
# Confirm the latest mapping is returned.
time.sleep(0.001)
mapping2 = copy.deepcopy(mapping)
- mapping2.start_time = self.now()
+ mapping2.start_time = now()
self.assertNotEqual(mapping, mapping2)
payload = mapping2.json()
r = requests.post(url, headers=self._ADMIN_HEADERS, data=payload)
@@ -546,7 +545,7 @@ def compare_mappings_ignore_end_time(self, m1: PhysicalToLogicalMapping, m2: Phy
def test_get_latest_mapping_from_physical(self):
pdev, new_pdev = self._create_physical_device(req_header=self._ADMIN_HEADERS)
ldev, new_ldev = self._create_default_logical_device(req_header=self._ADMIN_HEADERS)
- mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=self.now())
+ mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=now())
url = f'{_BASE}/mappings/'
@@ -590,7 +589,7 @@ def test_get_latest_mapping_from_physical(self):
def test_get_latest_mapping_from_logical(self):
pdev, new_pdev = self._create_physical_device(req_header=self._ADMIN_HEADERS)
ldev, new_ldev = self._create_default_logical_device(req_header=self._ADMIN_HEADERS)
- mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=self.now())
+ mapping = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=now())
url = f'{_BASE}/mappings/'
@@ -639,7 +638,7 @@ def test_get_all_logical_device_mappings(self):
# Using the DAO to create the test data, the REST API methods to do this are
# tested elsewhere.
- mapping1 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=self.now())
+ mapping1 = PhysicalToLogicalMapping(pd=new_pdev, ld=new_ldev, start_time=now())
dao.insert_mapping(mapping1)
time.sleep(0.1)
dao.end_mapping(ld=new_ldev.uid)
@@ -649,7 +648,7 @@ def test_get_all_logical_device_mappings(self):
pdev2.name = 'D2'
pdev2, new_pdev2 = self._create_physical_device(dev=pdev2, req_header=self._ADMIN_HEADERS)
time.sleep(0.1)
- mapping2 = PhysicalToLogicalMapping(pd=new_pdev2, ld=new_ldev, start_time=self.now())
+ mapping2 = PhysicalToLogicalMapping(pd=new_pdev2, ld=new_ldev, start_time=now())
dao.insert_mapping(mapping2)
time.sleep(0.1)
dao.end_mapping(ld=new_ldev.uid)
@@ -659,7 +658,7 @@ def test_get_all_logical_device_mappings(self):
pdev3.name = 'D3'
pdev3, new_pdev3 = self._create_physical_device(dev=pdev3, req_header=self._ADMIN_HEADERS)
time.sleep(0.1)
- mapping3 = PhysicalToLogicalMapping(pd=new_pdev3, ld=new_ldev, start_time=self.now())
+ mapping3 = PhysicalToLogicalMapping(pd=new_pdev3, ld=new_ldev, start_time=now())
dao.insert_mapping(mapping3)
url = f'{_BASE}/mappings/logical/all/{new_ldev.uid}'