1010from __future__ import annotations
1111
1212from datetime import datetime , timedelta
13- from typing import Generator , List
13+ from typing import Generator , List , BinaryIO
1414
1515from c8y_api ._base_api import CumulocityRestApi
1616from c8y_api .model ._base import CumulocityResource , SimpleObject , ComplexObject
@@ -28,7 +28,7 @@ class Event(ComplexObject):
2828 """
2929
3030 _resource = '/event/events'
31- # _accept can remain default
31+ _accept = 'application/vnd.com.nsn.cumulocity.event+json'
3232 _parser = ComplexObjectParser ({
3333 'type' : 'type' ,
3434 'time' : 'time' ,
@@ -37,8 +37,8 @@ class Event(ComplexObject):
3737 'updated_time' : 'lastUpdated' ,
3838 }, ['source' ])
3939
40- def __init__ (self , c8y : CumulocityRestApi = None , type : str = None , time : str | datetime = None ,
41- source : str = None , text : str = None , ** kwargs ): # noqa (type)
40+ def __init__ (self , c8y : CumulocityRestApi = None , type : str = None , time : str | datetime = None , # noqa (type)
41+ source : str = None , text : str = None , ** kwargs ):
4242 """Create a new Event object.
4343
4444 Args:
@@ -91,6 +91,9 @@ def updated_datetime(self) -> datetime:
9191 """
9292 return super ()._to_datetime (self .updated_time )
9393
94+ def _build_attachment_path (self ) -> str :
95+ return super ()._build_object_path () + '/binaries'
96+
9497 @classmethod
9598 def from_json (cls , json : dict ) -> Event :
9699 # (no doc update required)
@@ -146,6 +149,69 @@ def apply_to(self, other_id: str) -> Event:
146149 """
147150 return super ()._apply_to (other_id )
148151
152+ def has_attachment (self ) -> bool :
153+ """Check whether the event has a binary attachment.
154+
155+ Event objects that have an attachment feature a `c8y_IsBinary`
156+ fragment. This function checks the presence of that fragment.
157+
158+ Note: This does not query the database. Hence, the information might
159+ be outdated if a binary was attached _after_ the event object was
160+ last read from the database.
161+
162+ Returns:
163+ True if the event object has an attachment, False otherwise.
164+ """
165+ return 'c8y_IsBinary' in self
166+
167+ def download_attachment (self ) -> bytes :
168+ """Read the binary attachment.
169+
170+ Returns:
171+ The event's binary attachment as bytes.
172+ """
173+ super ()._assert_c8y ()
174+ super ()._assert_id ()
175+ return self .c8y .get_file (self ._build_attachment_path ())
176+
177+ def create_attachment (self , file : str | BinaryIO , content_type : str = None ) -> dict :
178+ """Create the binary attachment.
179+
180+ Args:
181+ file (str|BinaryIO): File-like object or a file path
182+ content_type (str): Content type of the file sent
183+ (default is application/octet-stream)
184+
185+ Returns:
186+ Attachment details as JSON object (dict).
187+ """
188+ super ()._assert_c8y ()
189+ super ()._assert_id ()
190+ return self .c8y .post_file (self ._build_attachment_path (), file ,
191+ accept = 'application/json' , content_type = content_type )
192+
193+ def update_attachment (self , file : str | BinaryIO , content_type : str = None ) -> dict :
194+ """Update the binary attachment.
195+
196+ Args:
197+ file (str|BinaryIO): File-like object or a file path
198+ content_type (str): Content type of the file sent
199+ (default is application/octet-stream)
200+
201+ Returns:
202+ Attachment details as JSON object (dict).
203+ """
204+ super ()._assert_c8y ()
205+ super ()._assert_id ()
206+ return self .c8y .put_file (self ._build_attachment_path (), file ,
207+ accept = 'application/json' , content_type = content_type )
208+
209+ def delete_attachment (self ):
210+ """Remove the binary attachment."""
211+ super ()._assert_c8y ()
212+ super ()._assert_id ()
213+ self .c8y .delete (self ._build_attachment_path ())
214+
149215
150216class Events (CumulocityResource ):
151217 """Provides access to the Events API.
@@ -159,6 +225,17 @@ class Events(CumulocityResource):
159225 def __init__ (self , c8y ):
160226 super ().__init__ (c8y , '/event/events' )
161227
228+ def build_attachment_path (self , event_id : str ) -> str :
229+ """Build the attachment path of a specific event.
230+
231+ Args:
232+ event_id (int|str): Database ID of the event
233+
234+ Returns:
235+ The relative path to the event attachment within Cumulocity.
236+ """
237+ return super ().build_object_path (event_id ) + '/binaries'
238+
162239 def get (self , event_id : str ) -> Event : # noqa (id)
163240 """Retrieve a specific object from the database.
164241
@@ -184,7 +261,7 @@ def select(self, type: str = None, source: str = None, fragment: str = None, #
184261 fetched from the database as long there is a consumer for them.
185262
186263 All parameters are considered to be filters, limiting the result set
187- to objects which meet the filters specification. Filters can be
264+ to objects which meet the filter's specification. Filters can be
188265 combined (within reason).
189266
190267 Args:
@@ -286,7 +363,7 @@ def delete_by(self, type: str = None, source: str = None, fragment: str = None,
286363 """Query the database and delete matching events.
287364
288365 All parameters are considered to be filters, limiting the result set
289- to objects which meet the filters specification. Filters can be
366+ to objects which meet the filter's specification. Filters can be
290367 combined (within reason).
291368
292369 Args:
@@ -306,3 +383,52 @@ def delete_by(self, type: str = None, source: str = None, fragment: str = None,
306383 # remove &page_number= from the end
307384 query = base_query [:base_query .rindex ('&' )]
308385 self .c8y .delete (query )
386+
387+ def create_attachment (self , event_id : str , file : str | BinaryIO , content_type : str = None ) -> dict :
388+ """Add an event's binary attachment.
389+
390+ Args:
391+ event_id (str): The database ID of the event
392+ file (str|BinaryIO): File-like object or a file path
393+ content_type (str): Content type of the file sent
394+ (default is application/octet-stream)
395+
396+ Returns:
397+ Attachment details as JSON object (dict).
398+ """
399+ return self .c8y .post_file (self .build_attachment_path (event_id ), file ,
400+ accept = 'application/json' , content_type = content_type )
401+
402+ def update_attachment (self , event_id : str , file : str | BinaryIO , content_type : str = None ) -> dict :
403+ """Update an event's binary attachment.
404+
405+ Args:
406+ event_id (str): The database ID of the event
407+ file (str|BinaryIO): File-like object or a file path
408+ content_type (str): Content type of the file sent
409+ (default is application/octet-stream)
410+
411+ Returns:
412+ Attachment details as JSON object (dict).
413+ """
414+ return self .c8y .put_file (self .build_attachment_path (event_id ), file ,
415+ accept = 'application/json' , content_type = content_type )
416+
417+ def download_attachment (self , event_id : str ) -> bytes :
418+ """Read an event's binary attachment.
419+
420+ Args:
421+ event_id (str): The database ID of the event
422+
423+ Returns:
424+ The event's binary attachment as bytes.
425+ """
426+ return self .c8y .get_file (self .build_attachment_path (event_id ))
427+
428+ def delete_attachment (self , event_id : str ):
429+ """Remove an event's binary attachment.
430+
431+ Args:
432+ event_id (str): The database ID of the event
433+ """
434+ self .c8y .delete (self .build_attachment_path (event_id ))
0 commit comments