Skip to content

Commit 7b62b83

Browse files
authored
adds handler for RW multiple properties (#125)
1 parent 986b5aa commit 7b62b83

File tree

2 files changed

+82
-3
lines changed

2 files changed

+82
-3
lines changed

hololinked/server/http/__init__.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
PropertyHandler,
4949
EventHandler,
5050
BaseHandler,
51+
RWMultiplePropertiesHandler,
5152
StopHandler,
5253
ThingDescriptionHandler,
5354
RPCHandler,
@@ -475,8 +476,6 @@ def add_action(
475476
if not issubklass(handler, BaseHandler):
476477
raise TypeError(f"handler should be subclass of BaseHandler, given type {type(handler)}")
477478
http_methods = _comply_http_method(http_method)
478-
if len(http_methods) != 1:
479-
raise ValueError("http_method should be a single HTTP method")
480479
if isinstance(action, Action):
481480
action = action.to_affordance() # type: ActionAffordance
482481
kwargs["resource"] = action
@@ -741,6 +740,7 @@ def add_interaction_affordances(
741740
path = f"/{pep8_to_dashed_name(event.name)}"
742741
self.server.add_event(URL_path=path, event=event, handler=self.server.event_handler)
743742

743+
# thing description handler
744744
get_thing_model_action = next((action for action in actions if action.name == "get_thing_model"), None)
745745
get_thing_description_action = deepcopy(get_thing_model_action)
746746
get_thing_description_action.override_defaults(name="get_thing_description")
@@ -750,6 +750,21 @@ def add_interaction_affordances(
750750
http_method=("GET",),
751751
handler=ThingDescriptionHandler,
752752
)
753+
754+
# RW multiple properties handler
755+
read_properties = Thing._get_properties.to_affordance(Thing)
756+
write_properties = Thing._set_properties.to_affordance(Thing)
757+
read_properties.override_defaults(thing_id=get_thing_model_action.thing_id)
758+
write_properties.override_defaults(thing_id=get_thing_model_action.thing_id)
759+
self.server.add_action(
760+
URL_path=f"/{thing_id}/properties" if thing_id else "/properties",
761+
action=read_properties,
762+
http_method=("GET", "PUT", "PATCH"),
763+
handler=RWMultiplePropertiesHandler,
764+
read_properties_resource=read_properties,
765+
write_properties_resource=write_properties,
766+
)
767+
753768
self.server.logger.debug(
754769
f"added thing description action for thing id {thing_id if thing_id else 'unknown'} at path "
755770
+ f"{f'/{thing_id}/resources/wot-td' if thing_id else '/resources/wot-td'}"

hololinked/server/http/handlers.py

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,34 @@ async def delete(self) -> None:
464464
self.finish()
465465

466466

467+
class RWMultiplePropertiesHandler(ActionHandler):
468+
def initialize(self, resource, owner_inst=None, metadata=None, **kwargs) -> None:
469+
self.read_properties_resource = kwargs.pop("read_properties_resource", None)
470+
self.write_properties_resource = kwargs.pop("write_properties_resource", None)
471+
return super().initialize(resource, owner_inst, metadata)
472+
473+
async def get(self) -> None:
474+
if self.is_method_allowed("GET"):
475+
self.resource = self.read_properties_resource
476+
if self.message_id is not None:
477+
await self.handle_no_block_response()
478+
else:
479+
await self.handle_through_thing(Operations.invokeaction)
480+
self.finish()
481+
482+
async def put(self) -> None:
483+
if self.is_method_allowed("PUT"):
484+
self.resource = self.write_properties_resource
485+
await self.handle_through_thing(Operations.invokeaction)
486+
self.finish()
487+
488+
async def patch(self) -> None:
489+
if self.is_method_allowed("PATCH"):
490+
self.resource = self.write_properties_resource
491+
await self.handle_through_thing(Operations.invokeaction)
492+
self.finish()
493+
494+
467495
class EventHandler(BaseHandler):
468496
"""handles events emitted by `Thing` and tunnels them as HTTP SSE"""
469497

@@ -722,11 +750,11 @@ def generate_td(
722750
) -> dict[str, JSONSerializable]:
723751
TD = copy.deepcopy(TM)
724752
# sanitize some things
725-
TD["id"] = f"{self.server.router.get_basepath(authority=authority, use_localhost=use_localhost)}/{TD['id']}"
726753

727754
self.add_properties(TD, TM, authority=authority, use_localhost=use_localhost)
728755
self.add_actions(TD, TM, authority=authority, use_localhost=use_localhost)
729756
self.add_events(TD, TM, authority=authority, use_localhost=use_localhost)
757+
self.add_top_level_forms(TD, authority=authority, use_localhost=use_localhost)
730758

731759
self.add_security_definitions(TD)
732760
return TD
@@ -820,6 +848,42 @@ def add_events(
820848
form.subprotocol = "sse"
821849
TD["events"][name]["forms"].append(form.json())
822850

851+
def add_top_level_forms(self, TD: dict[str, JSONSerializable], authority: str, use_localhost: bool) -> None:
852+
"""adds top level forms for reading and writing multiple properties"""
853+
854+
properties_end_point = f"{self.server.router.get_basepath(authority, use_localhost)}/{TD['id']}/properties"
855+
856+
if TD.get("forms", None) is None:
857+
TD["forms"] = []
858+
859+
readallproperties = Form()
860+
readallproperties.href = properties_end_point
861+
readallproperties.op = "readallproperties"
862+
readallproperties.htv_methodName = "GET"
863+
readallproperties.contentType = "application/json"
864+
TD["forms"].append(readallproperties.json())
865+
866+
writeallproperties = Form()
867+
writeallproperties.href = properties_end_point
868+
writeallproperties.op = "writeallproperties"
869+
writeallproperties.htv_methodName = "PUT"
870+
writeallproperties.contentType = "application/json"
871+
TD["forms"].append(writeallproperties.json())
872+
873+
readmultipleproperties = Form()
874+
readmultipleproperties.href = properties_end_point
875+
readmultipleproperties.op = "readmultipleproperties"
876+
readmultipleproperties.htv_methodName = "GET"
877+
readmultipleproperties.contentType = "application/json"
878+
TD["forms"].append(readmultipleproperties.json())
879+
880+
writemultipleproperties = Form()
881+
writemultipleproperties.href = properties_end_point
882+
writemultipleproperties.op = "writemultipleproperties"
883+
writemultipleproperties.htv_methodName = "PATCH"
884+
writemultipleproperties.contentType = "application/json"
885+
TD["forms"].append(writemultipleproperties.json())
886+
823887
def add_security_definitions(self, TD: dict[str, JSONSerializable]) -> None:
824888
from ...td.security_definitions import SecurityScheme
825889

0 commit comments

Comments
 (0)