Skip to content

Commit

Permalink
add support of alterationtypes in subscriptions model
Browse files Browse the repository at this point in the history
198 support of alterationtypes in subscriptions
  • Loading branch information
djs0109 authored Jul 18, 2024
2 parents 6c323dc + 50c990d commit a10516b
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### v0.4.2
- update: allow duplicated name in device, check uniqueness of object_id ([#279](https://github.com/RWTH-EBC/FiLiP/pull/279))
- add: support alterationTypes in subscription model ([#293](https://github.com/RWTH-EBC/FiLiP/pull/293))
- add: validation for JEXL based expression ([#260](https://github.com/RWTH-EBC/FiLiP/pull/260))
- add: tutorials for multi-entity ([#260](https://github.com/RWTH-EBC/FiLiP/pull/260))
- add: add ``update_entity_relationships`` to allow relationship update ([#271](https://github.com/RWTH-EBC/FiLiP/pull/271))
Expand Down
21 changes: 21 additions & 0 deletions filip/models/ngsi_v2/subscriptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,8 @@ class Condition(BaseModel):
expression matches.
If neither attrs nor expression are used, a notification is sent whenever
any of the attributes of the entity changes.
alterationTypes: for more information about this field, see
https://github.com/telefonicaid/fiware-orion/blob/3.8.0/doc/manuals/orion-api.md#subscriptions-based-in-alteration-type
"""
attrs: Optional[Union[str, List[str]]] = Field(
Expand All @@ -388,6 +390,10 @@ class Condition(BaseModel):
'coords (see "List entities" operation above about this '
'field).'
)
alterationTypes: Optional[List[str]] = Field(
default=None,
description='list of alteration types triggering the subscription'
)

@field_validator('attrs')
def check_attrs(cls, v):
Expand All @@ -398,6 +404,21 @@ def check_attrs(cls, v):
else:
raise TypeError()

@field_validator('alterationTypes')
def check_alteration_types(cls, v):
allowed_types = {"entityCreate", "entityDelete", "entityUpdate", "entityChange"}

if v is None:
return None
elif isinstance(v, list):
for item in v:
if item not in allowed_types:
raise ValueError(f'{item} is not a valid alterationType'
f' allowed values are {allowed_types}')
return v
else:
raise ValueError('alterationTypes must be a list of strings')


class Subject(BaseModel):
"""
Expand Down
98 changes: 97 additions & 1 deletion tests/clients/test_ngsi_v2_cb.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@

from filip.models.ngsi_v2.base import AttrsFormat, EntityPattern, Status, \
NamedMetadata
from filip.models.ngsi_v2.subscriptions import Mqtt, Message, Subscription
from filip.models.ngsi_v2.subscriptions import Mqtt, Message, Subscription, Condition, \
Notification
from filip.models.ngsi_v2.iot import \
Device, \
DeviceCommand, \
Expand Down Expand Up @@ -667,6 +668,101 @@ def test_subscription_set_status(self):
sub_res_expired = client.get_subscription(subscription_id=sub_id)
self.assertEqual(sub_res_expired.status, Status.EXPIRED)

@clean_test(fiware_service=settings.FIWARE_SERVICE,
fiware_servicepath=settings.FIWARE_SERVICEPATH,
cb_url=settings.CB_URL,
iota_url=settings.IOTA_JSON_URL)
def test_subscription_alterationtypes(self):
"""
Test behavior of subscription alterationtypes since Orion 3.7.0
"""
sub = self.subscription.model_copy()
sub.subject.condition = Condition(alterationTypes=[])
sub.notification = Notification(
mqtt=Mqtt(
url=settings.MQTT_BROKER_URL_INTERNAL,
topic="test/alterationtypes"))
test_entity = ContextEntity(id="test:alterationtypes", type="Room",
temperature={"type": "Number",
"value": 25.0}
)

# test default with empty alterationTypes, triggered during actual change
self.client.post_entity(test_entity)
sub_id_default = self.client.post_subscription(subscription=sub)
test_entity = self.client.get_entity(entity_id=test_entity.id)
test_entity.temperature.value = 26.0
self.client.update_entity(test_entity)
time.sleep(1)
sub_result_default = self.client.get_subscription(sub_id_default)
self.assertEqual(sub_result_default.notification.timesSent, 1)
# not triggered during with no actual update
self.client.update_entity(test_entity)
time.sleep(1)
sub_result_default = self.client.get_subscription(sub_id_default)
self.assertEqual(sub_result_default.notification.timesSent, 1)
self.client.delete_subscription(sub_id_default)

# test entityChange
sub.subject.condition.alterationTypes = ["entityChange"]
sub_id_change = self.client.post_subscription(subscription=sub)
test_entity.temperature.value = 27.0
self.client.update_entity(test_entity)
time.sleep(1)
sub_result_change = self.client.get_subscription(sub_id_change)
self.assertEqual(sub_result_change.notification.timesSent, 1)
# not triggered during with no actual update
self.client.update_entity(test_entity)
time.sleep(1)
sub_result_change = self.client.get_subscription(sub_id_change)
self.assertEqual(sub_result_change.notification.timesSent, 1)
self.client.delete_subscription(sub_id_change)

# test entityCreate
test_entity_create = ContextEntity(id="test:alterationtypes2", type="Room",
temperature={"type": "Number",
"value": 25.0}
)
sub.subject.condition.alterationTypes = ["entityCreate"]
sub_id_create = self.client.post_subscription(subscription=sub)
self.client.post_entity(test_entity_create)
time.sleep(1)
sub_result_create = self.client.get_subscription(sub_id_create)
self.assertEqual(sub_result_create.notification.timesSent, 1)
# not triggered during when update
test_entity_create = self.client.get_entity(entity_id=test_entity_create.id)
test_entity_create.temperature.value = 26.0
self.client.update_entity(test_entity_create)
time.sleep(1)
sub_result_create = self.client.get_subscription(sub_id_create)
self.assertEqual(sub_result_create.notification.timesSent, 1)
self.client.delete_subscription(sub_id_create)

# test entityDelete
sub.subject.condition.alterationTypes = ["entityDelete"]
sub_id_delete = self.client.post_subscription(subscription=sub)
self.client.delete_entity(test_entity_create.id)
time.sleep(1)
sub_result_delete = self.client.get_subscription(sub_id_delete)
self.assertEqual(sub_result_delete.notification.timesSent, 1)
self.client.delete_subscription(sub_id_delete)

# test entityUpdate (no matter if the entity actually changed or not)
sub.subject.condition.alterationTypes = ["entityUpdate"]
sub_id_update = self.client.post_subscription(subscription=sub)
# triggered when actual change
test_entity.temperature.value = 28.0
self.client.update_entity(test_entity)
time.sleep(1)
sub_result_update = self.client.get_subscription(sub_id_update)
self.assertEqual(sub_result_update.notification.timesSent, 1)
# triggered when no actual change
self.client.update_entity(test_entity)
time.sleep(1)
sub_result_update = self.client.get_subscription(sub_id_update)
self.assertEqual(sub_result_update.notification.timesSent, 2)
self.client.delete_subscription(sub_id_update)

@clean_test(fiware_service=settings.FIWARE_SERVICE,
fiware_servicepath=settings.FIWARE_SERVICEPATH,
cb_url=settings.CB_URL,
Expand Down
18 changes: 17 additions & 1 deletion tests/models/test_ngsi_v2_subscriptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
Notification, \
Subscription, \
NgsiPayload, \
NgsiPayloadAttr
NgsiPayloadAttr, Condition
from filip.models.base import FiwareHeader
from filip.utils.cleanup import clear_all, clean_test
from tests.config import settings
Expand Down Expand Up @@ -270,6 +270,22 @@ def test_model_dump_json(self):
with self.assertRaises(KeyError):
_ = test_dict["status"]

def test_alteration_types_model(self):
c = Condition(alterationTypes=["entityCreate", "entityDelete"])
# entity override is not a valid alteration type
with self.assertRaises(ValueError):
c = Condition(alterationTypes=["entityOverride", "entityDelete"])
# test alteration types with different input types
# list success
c = Condition(alterationTypes=["entityCreate", "entityDelete"])
# tuple success
c = Condition(alterationTypes=("entityChange", "entityDelete"))
# set success
c = Condition(alterationTypes={"entityUpdate", "entityDelete"})
# str fail
with self.assertRaises(ValueError):
c = Condition(alterationTypes="entityCreate")

def tearDown(self) -> None:
"""
Cleanup test server
Expand Down

0 comments on commit a10516b

Please sign in to comment.