1010from typing import Any , Generator , List
1111
1212from c8y_api .model ._base import CumulocityResource
13+ from c8y_api .model ._util import _QueryUtil
1314from c8y_api .model .managedobjects import ManagedObjectUtil , ManagedObject , Device , DeviceGroup
1415
1516
@@ -52,10 +53,10 @@ def get_all(self, type: str = None, fragment: str = None, name: str = None, owne
5253 Returns:
5354 List of ManagedObject instances
5455 """
55- return list (self .select (type = type , fragment = fragment , name = name , limit = limit , page_size = page_size ))
56+ return list (self .select (type = type , fragment = fragment , name = name , owner = owner , limit = limit , page_size = page_size ))
5657
5758 def select (self , type : str = None , fragment : str = None , name : str = None , owner : str = None , # noqa (type)
58- limit : int = None , page_size : int = 1000 ) -> Generator [ManagedObject ]:
59+ query : str = None , limit : int = None , page_size : int = 1000 ) -> Generator [ManagedObject ]:
5960 """ Query the database for managed objects and iterate over the
6061 results.
6162
@@ -70,7 +71,12 @@ def select(self, type: str = None, fragment: str = None, name: str = None, owner
7071 type (str): Managed object type
7172 fragment (str): Name of a present custom/standard fragment
7273 name (str): Name of the managed object
74+ Note: The Cumulocity REST API does not support filtering for
75+ names directly; this is a convenience parameter which will
76+ translate all filters into a query string.
7377 owner (str): Username of the object owner
78+ query (str): Complex query to execute; all other filters are
79+ ignored if such a custom query is provided
7480 limit (int): Limit the number of results to this number.
7581 page_size (int): Define the number of events which are read (and
7682 parsed in one chunk). This is a performance related setting.
@@ -79,12 +85,37 @@ def select(self, type: str = None, fragment: str = None, name: str = None, owner
7985 Generator for ManagedObject instances
8086 """
8187 return self ._select (ManagedObject .from_json , type = type , fragment = fragment , name = name , owner = owner ,
82- limit = limit , page_size = page_size )
88+ query = None , limit = limit , page_size = page_size )
89+
90+ def _select (self , jsonyfy_func , type : str = None , fragment : str = None , name : str = None , # noqa
91+ owner : str = None , query : str = None , limit : int = None , page_size : int = 1000 ) -> Generator [Any ]:
92+
93+ query_filters = []
94+
95+ # if there is no custom query, we check whether standard filters need to
96+ # be translated into a query
97+ if not query and name :
98+
99+ # A name filter can only be expressed as a query, which then
100+ # triggers "query mode" (all filters are translated into a query)
101+ query_filters .append (f"name eq '{ _QueryUtil .encode_odata_query_value (name )} '" )
102+
103+ if type :
104+ query_filters .append (f"type eq '{ type } '" )
105+ if owner :
106+ query_filters .append (f"owner eq '{ owner } '" )
107+ if fragment :
108+ query_filters .append (f"has({ fragment } )" )
109+ if len (query_filters ) == 1 :
110+ query = query_filters [0 ]
111+ else :
112+ query = '$filter=(' + ' and ' .join (query_filters ) + ')'
113+
114+ if query :
115+ base_query = self ._build_base_query (query = query , page_size = page_size )
116+ else :
117+ base_query = self ._build_base_query (type = type , fragment = fragment , owner = owner , page_size = page_size )
83118
84- def _select (self , jsonyfy_func , type : str = None , fragment : str = None , name : str = None ,
85- owner : str = None , limit : int = None , page_size : int = 1000 ) -> Generator [Any ]:
86- base_query = self ._build_base_query (type = type , fragment = fragment , owner = owner ,
87- query = f"name eq '{ name } '" if name else None , page_size = page_size )
88119 return super ()._iterate (base_query , limit , jsonyfy_func )
89120
90121 def create (self , * objects : ManagedObject ):
@@ -166,7 +197,7 @@ def get(self, id: str) -> Device: # noqa (id)
166197 return device
167198
168199 def select (self , type : str = None , name : str = None , owner : str = None , # noqa (type, args)
169- limit : int = None , page_size : int = 100 ) -> Generator [Device ]:
200+ query : str = None , limit : int = None , page_size : int = 100 ) -> Generator [Device ]:
170201 # pylint: disable=arguments-differ
171202 """ Query the database for devices and iterate over the results.
172203
@@ -180,7 +211,12 @@ def select(self, type: str = None, name: str = None, owner: str = None, # noqa
180211 Args:
181212 type (str): Device type
182213 name (str): Name of the device
214+ Note: The Cumulocity REST API does not support filtering for
215+ names directly; this is a convenience parameter which will
216+ translate all filters into a query string.
183217 owner (str): Username of the object owner
218+ query (str): Complex query to execute; all other filters are
219+ ignored if such a custom query is provided
184220 limit (int): Limit the number of results to this number.
185221 page_size (int): Define the number of events which are read (and
186222 parsed in one chunk). This is a performance related setting.
@@ -189,7 +225,7 @@ def select(self, type: str = None, name: str = None, owner: str = None, # noqa
189225 Generator for Device objects
190226 """
191227 return self ._select (ManagedObject .from_json , type = type , fragment = 'c8y_IsDevice' , name = name , owner = owner ,
192- limit = limit , page_size = page_size )
228+ query = query , limit = limit , page_size = page_size )
193229
194230 def get_all (self , type : str = None , name : str = None , owner : str = None , # noqa (type, parameters)
195231 page_size : int = 100 ) -> List [Device ]:
@@ -244,8 +280,8 @@ def get(self, group_id):
244280 group .c8y = self .c8y
245281 return group
246282
247- def select (self , type : str = DeviceGroup .ROOT_TYPE , parent : str | int = None , fragment : str = None ,
248- name : str = None , owner : str = None , page_size : int = 100 ) -> Generator [DeviceGroup ]:
283+ def select (self , type : str = DeviceGroup .ROOT_TYPE , parent : str | int = None , fragment : str = None , # noqa
284+ name : str = None , owner : str = None , query : str = None , page_size : int = 100 ) -> Generator [DeviceGroup ]:
249285 # pylint: disable=arguments-differ, arguments-renamed
250286 """ Select device groups by various parameters.
251287
@@ -263,41 +299,55 @@ def select(self, type: str = DeviceGroup.ROOT_TYPE, parent: str | int = None, fr
263299 match device groups only you need to use the fragment filter.
264300 parent (str): ID of the parent device group
265301 Note: this forces the `type` filter to be c8y_DeviceSubGroup
302+ Like the `name` parameter, this is a convenience parameter
303+ which will translate all filters into a query string.
266304 fragment (str): Additional fragment present within the objects
267- name (str): Name string of the groups to select; no partial
268- matching/patterns are supported
305+ name (str): Name string of the groups to select
306+ Note: he Cumulocity REST API does not support filtering for
307+ names directly; this is a convenience parameter which will
308+ translate all filters into a query string.
309+ No partial matching/patterns are supported
269310 owner (str): Username of the group owner
311+ query (str): Complex query to execute; all other filters are
312+ ignored if such a custom query is provided
270313 page_size (int): Define the number of events which are read (and
271314 parsed in one chunk). This is a performance related setting.
272315
273316 Returns:
274317 Generator of DeviceGroup instances
275318 """
276319 query_filters = []
277- if name :
278- query_filters .append (f"name eq '{ name } '" )
279- if parent :
280- query_filters .append (f"bygroupid({ parent } )" )
281- type = DeviceGroup .CHILD_TYPE
282-
283- # if any query was defined, all filters must be put into the query
284- if query_filters :
285- query_filters .append (f"type eq { type } " )
286- # all other filters must be set as well
287- if fragment :
288- query_filters .append (f"has({ fragment } )" )
289- if owner :
290- query_filters .append (f"owner eq '{ owner } '" )
291- query = '$filter=' + ' and ' .join (query_filters )
292320
321+ # if there is no custom query, we check whether standard filters need to
322+ # be translated into a query
323+ if not query :
324+
325+ # Both name and parent filters can only be expressed as a query,
326+ # which then triggers "query mode"
327+ if name :
328+ query_filters .append (f"name eq '{ _QueryUtil .encode_odata_query_value (name )} '" )
329+ if parent :
330+ query_filters .append (f"bygroupid({ parent } )" )
331+ type = DeviceGroup .CHILD_TYPE # noqa
332+
333+ # if any query was defined, all filters must be put into the query
334+ if query_filters :
335+ if type :
336+ query_filters .append (f"type eq '{ type } '" )
337+ if owner :
338+ query_filters .append (f"owner eq '{ owner } '" )
339+ if fragment :
340+ query_filters .append (f"has({ fragment } " )
341+ query = '$filter=' + ' and ' .join (query_filters )
342+
343+ if query :
293344 base_query = self ._build_base_query (query = query , page_size = page_size )
294- # otherwise we can just build the regular query
295345 else :
296346 base_query = self ._build_base_query (type = type , fragment = fragment , owner = owner , page_size = page_size )
297347
298348 return super ()._iterate (base_query , limit = 9999 , parse_func = DeviceGroup .from_json )
299349
300- def get_all (self , type : str = DeviceGroup .ROOT_TYPE , parent : str | int = None , fragment : str = None ,
350+ def get_all (self , type : str = DeviceGroup .ROOT_TYPE , parent : str | int = None , fragment : str = None , # noqa
301351 name : str = None , owner : str = None , page_size : int = 100 ): # noqa
302352 # pylint: disable=arguments-differ, arguments-renamed
303353 """ Select managed objects by various parameters.
@@ -328,9 +378,9 @@ def assign_children(self, root_id, *child_ids):
328378 # adding multiple references at once is not (yet) supported
329379 # refs = {'references': [InventoryUtil.build_managed_object_reference(id) for id in child_ids]}
330380 # self.c8y.post(self.build_object_path(root_id) + '/childAssets', json=refs, accept='')
331- for id in child_ids :
381+ for child_id in child_ids :
332382 self .c8y .post (self .build_object_path (root_id ) + '/childAssets' ,
333- json = ManagedObjectUtil .build_managed_object_reference (id ), accept = '' )
383+ json = ManagedObjectUtil .build_managed_object_reference (child_id ), accept = '' )
334384
335385 def unassign_children (self , root_id , * child_ids ):
336386 """Unlink child groups from this device group.
@@ -339,7 +389,7 @@ def unassign_children(self, root_id, *child_ids):
339389 root_id (str|int): ID of the root device group
340390 child_ids (*str|int): ID of the child device groups
341391 """
342- refs = {'references' : [ManagedObjectUtil .build_managed_object_reference (id ) for id in child_ids ]}
392+ refs = {'references' : [ManagedObjectUtil .build_managed_object_reference (i ) for i in child_ids ]}
343393 self .c8y .delete (self .build_object_path (root_id ) + '/childAssets' , json = refs )
344394
345395 def delete (self , * groups : DeviceGroup | str | int ):
0 commit comments