Auto-generated patch by python-black
[integration/test.git] / csit / libraries / IoTDM / client_libs / onem2m_json_primitive.py
1 """
2  Specific implementation of OneM2MPrimitive abstract class which uses JSON
3  strings and dictionaries as well as JSON pointers to store and access data
4  as OneM2M primitive objects
5 """
6
7 #
8 # Copyright (c) 2017 Cisco Systems, Inc. and others.  All rights reserved.
9 #
10 # This program and the accompanying materials are made available under the
11 # terms of the Eclipse Public License v1.0 which accompanies this distribution,
12 # and is available at http://www.eclipse.org/legal/epl-v10.html
13 #
14
15 import json
16
17 from onem2m_primitive import OneM2M
18 from onem2m_primitive import OneM2MPrimitive
19 from onem2m_primitive import OneM2MPrimitiveBuilder
20 from onem2m_primitive import OneM2MPrimitiveBuilderException
21 from jsonpointer import JsonPointer
22 from jsonpointer import JsonPointerException
23
24
25 class OneM2MJsonPrimitive(OneM2MPrimitive):
26     """
27     Implementation of OneM2M primitive which allows to use JSON as strings or dictionaries
28     to work with the request/response primitives.
29     Using particular encoder/decoder, this primitive can be encoded/decoded to/from desired
30     content type:
31         JSON short scheme, JSON long scheme,
32         XML short scheme, XML long scheme
33     """
34
35     def __init__(
36         self, parameters, content, protocol_name, protocol_parameters, short_scheme=True
37     ):
38         self.parameters = parameters
39         self.content = content
40         self.protocol = protocol_name
41         self.proto_params = protocol_parameters
42         self.short_scheme = short_scheme
43
44     def get_parameters(self):
45         return self.parameters
46
47     def get_parameters_str(self):
48         return json.dumps(self.parameters)
49
50     def _create_json_pointer(self, pointer_string):
51         try:
52             json_pointer = str(pointer_string)
53             # add leading slash if missing
54             if json_pointer[0] != "/":
55                 json_pointer = "/" + json_pointer
56
57             # remove slash from the end if exists
58             if json_pointer[-1] == "/":
59                 json_pointer = json_pointer[:-1]
60
61             json_pointer = JsonPointer(json_pointer)
62         except Exception as e:
63             raise RuntimeError(
64                 "Invalid JSON pointer passed: {}, error: {}".format(
65                     pointer_string, e.message
66                 )
67             )
68         return json_pointer
69
70     def _get_item_by_pointer(self, data_dict, pointer):
71         if None is data_dict:
72             raise AttributeError("No JSON data passed")
73
74         if not isinstance(pointer, JsonPointer):
75             json_pointer = self._create_json_pointer(pointer)
76         else:
77             json_pointer = pointer
78
79         try:
80             item = json_pointer.resolve(data_dict)
81         except JsonPointerException as e:
82             raise RuntimeError(
83                 "Failed to get JSON item by JSON pointer: {}, error: {}".format(
84                     pointer, e.message
85                 )
86             )
87
88         return item
89
90     def _has_item_by_pointer(self, data_dict, pointer):
91         if None is data_dict:
92             raise AttributeError("No JSON data passed")
93
94         if not isinstance(pointer, JsonPointer):
95             json_pointer = self._create_json_pointer(pointer)
96         else:
97             json_pointer = pointer
98
99         try:
100             json_pointer.resolve(data_dict)
101         except JsonPointerException:
102             return False
103
104         return True
105
106     def get_param(self, param):
107         """Returns container or item value identified by string or JsonPointer object"""
108         return self._get_item_by_pointer(self.parameters, param)
109
110     def has_param(self, param):
111         """Returns True if parameter identified by string or JsonPointer object exists, False otherwise"""
112         return self._has_item_by_pointer(self.parameters, param)
113
114     def get_content(self):
115         return self.content
116
117     def get_content_str(self):
118         if not self.content:
119             return ""
120         return json.dumps(self.content)
121
122     def get_attr(self, attr):
123         """Returns container or item value identified by string or JsonPointer object"""
124         return self._get_item_by_pointer(self.content, attr)
125
126     def has_attr(self, attr):
127         """Returns True if attribute identified by string or JsonPointer object exists, False otherwise"""
128         return self._has_item_by_pointer(self.content, attr)
129
130     def get_protocol_specific_parameters(self):
131         return self.proto_params
132
133     def get_protocol_specific_parameters_str(self):
134         return json.dumps(self.proto_params)
135
136     def get_proto_param(self, proto_param):
137         """Returns container or item value identified by string or JsonPointer object"""
138         return self._get_item_by_pointer(self.proto_params, proto_param)
139
140     def has_proto_param(self, proto_param):
141         """Returns True if parameter identified by string or JsonPointer object exists, False otherwise"""
142         return self._has_item_by_pointer(self.proto_params, proto_param)
143
144     def get_primitive_str(self):
145         """
146         Returns whole OneM2M primitive as JSON string including primitive
147         parameters and primitive content
148         """
149         primitive = {}
150         if self.parameters:
151             primitive = self.parameters.copy()
152
153         if self.content:
154             primitive[OneM2M.short_primitive_content] = self.content.copy()
155
156         return json.dumps(primitive)
157
158     def get_communication_protocol(self):
159         return self.protocol
160
161     def _check_protocol_of_request(self):
162         if not self.get_communication_protocol():
163             raise AssertionError("Communication protocol of request primitive not set")
164
165     def _check_protocol_of_response(self, response_primitive):
166         if not response_primitive.get_communication_protocol():
167             raise AssertionError("Communication protocol of response primitive not set")
168
169     def _check_exchange_protocols(self, response_primitive):
170         self._check_protocol_of_request()
171         self._check_protocol_of_response(response_primitive)
172         if (
173             not self.get_communication_protocol()
174             == response_primitive.get_communication_protocol()
175         ):
176             raise AssertionError(
177                 "Request {} and response {} primitives' communication protocols doesn't match.".format(
178                     self.get_communication_protocol(),
179                     response_primitive.get_communication_protocol(),
180                 )
181             )
182
183     def _check_request_common(self):
184         op = self.get_param(OneM2M.short_operation)
185         if not op:
186             raise AssertionError("Request primitive without operation set")
187
188         if not isinstance(op, int):
189             raise AssertionError(
190                 "Invalid data type ({}) of operation where integer is expected".format(
191                     op.__class__
192                 )
193             )
194
195         if op not in OneM2M.operation_valid_values:
196             raise AssertionError(
197                 "Request primitive with unknown operation set: {}".format(op)
198             )
199
200         rqi = self.get_param(OneM2M.short_request_identifier)
201         if not rqi:
202             raise AssertionError("Request primitive without request id")
203
204         if not isinstance(rqi, basestring):
205             raise AssertionError(
206                 "Invalid data type ({}) of request identifier where string is expected".format(
207                     rqi.__class__
208                 )
209             )
210         return op, rqi
211
212     def _check_response_common(self, response_primitive, rqi=None, rsc=None):
213         rsp_rqi = response_primitive.get_param(OneM2M.short_request_identifier)
214         if not rsp_rqi:
215             raise AssertionError("Response primitive without request id")
216
217         if not isinstance(rsp_rqi, basestring):
218             raise AssertionError(
219                 "Invalid data type ({}) of request identifier where string is expected".format(
220                     rsp_rqi.__class__
221                 )
222             )
223
224         if rqi and rqi != rsp_rqi:
225             raise AssertionError(
226                 "Request IDs mismatch: req: {}, rsp: {}".format(rqi, rsp_rqi)
227             )
228
229         r_rsc = response_primitive.get_param(OneM2M.short_response_status_code)
230         if not r_rsc:
231             raise AssertionError("Response primitive without status code")
232
233         if not isinstance(r_rsc, int):
234             raise AssertionError(
235                 "Invalid data type ({}) of response status code where integer is expected".format(
236                     r_rsc.__class__
237                 )
238             )
239
240         if r_rsc not in OneM2M.supported_result_codes:
241             raise AssertionError(
242                 "Unsupported response primitive result code: {}".format(r_rsc)
243             )
244
245         if None is not rsc:
246             if r_rsc != rsc:
247                 raise AssertionError(
248                     "Unexpected result code: {}, expected: {}".format(r_rsc, rsc)
249                 )
250
251         return r_rsc
252
253     def _check_exchange_common(self, response_primitive, rsc=None):
254         self._check_exchange_protocols(response_primitive)
255         op, rqi = self._check_request_common()
256         r_rsc = self._check_response_common(response_primitive, rqi, rsc)
257         return op, r_rsc
258
259     def _check_response_positive_result(
260         self, response_rsc=None, request_operation=None
261     ):
262         if response_rsc and response_rsc not in OneM2M.positive_result_codes:
263             raise AssertionError(
264                 "Response with negative status code: {}".format(response_rsc)
265             )
266
267         if None is request_operation:
268             return
269
270         expected_rsc = OneM2M.expected_result_codes[request_operation]
271         if expected_rsc != response_rsc:
272             raise AssertionError(
273                 "Unexpected positive result code for operation: {}, received: {}, expected: {}".format(
274                     request_operation, response_rsc, expected_rsc
275                 )
276             )
277
278     def check_exchange(self, response_primitive, rsc=None):
279         op, r_rsc = self._check_exchange_common(response_primitive, rsc)
280         self._check_response_positive_result(r_rsc, op)
281
282     def _check_response_negative_result(self, response_primitive, error_message):
283         if not response_primitive:
284             raise AttributeError("Response primitive not passed")
285
286         if not error_message:
287             return
288
289         msg = response_primitive.get_attr(OneM2M.error_message_item)
290         if not msg:
291             raise AssertionError(
292                 "Negative response primitive without error message, expected message: {}".format(
293                     error_message
294                 )
295             )
296
297         if not isinstance(msg, basestring):
298             raise AssertionError(
299                 "Invalid data type ({}) of response error message where string is expected".format(
300                     msg.__class__
301                 )
302             )
303
304         if not msg == error_message:
305             raise AssertionError(
306                 "Negative response with unexpected error message: {}, expected: {}".format(
307                     msg, error_message
308                 )
309             )
310
311     def check_exchange_negative(self, response_primitive, rsc, error_message=None):
312         op, r_rsc = self._check_exchange_common(response_primitive, rsc)
313         self._check_response_negative_result(response_primitive, error_message)
314
315     def check_request(self):
316         self._check_protocol_of_request()
317         self._check_request_common()
318
319     def check_response(self, rqi=None, rsc=None, request_operation=None):
320         self._check_protocol_of_response(self)
321         self._check_response_common(self, rqi, rsc)
322         self._check_response_positive_result(rsc, request_operation)
323
324     def check_response_negative(self, rqi=None, rsc=None, error_message=None):
325         self._check_protocol_of_response(self)
326         self._check_response_common(self, rqi, rsc)
327         self._check_response_negative_result(self, error_message)
328
329     def _compare(self, primitive2):
330         raise NotImplementedError()
331
332
333 class OneM2MJsonPrimitiveBuilder(OneM2MPrimitiveBuilder, OneM2MJsonPrimitive):
334     """Generic implementation of builder class for OneM2M JSON primitives"""
335
336     def __init__(self):
337         self.parameters = {}
338         self.content = {}
339         self.protocol = None
340         self.proto_params = {}
341         self.short_scheme = None
342
343     def _prepare_params(self, params):
344         if not params:
345             return {}
346
347         if isinstance(params, unicode):
348             params = str(params)
349
350         if isinstance(params, basestring):
351             params = json.loads(params)
352             return params
353
354         if isinstance(params, dict):
355             return params.copy()
356
357         raise OneM2MPrimitiveBuilderException("Unsupported parameters object type")
358
359     def set_parameters(self, parameters):
360         self.parameters = self._prepare_params(parameters)
361         return self
362
363     def append_parameters(self, parameters):
364         if not parameters:
365             return self
366         parameters = self._prepare_params(parameters)
367         self.parameters.update(parameters)
368         return self
369
370     def set_param(self, param_name, param_value):
371         self.parameters.update({param_name: param_value})
372         return self
373
374     def set_content(self, attributes):
375         self.content = self._prepare_params(attributes)
376         return self
377
378     def append_content_attributes(self, attributes):
379         if not attributes:
380             return self
381         attributes = self._prepare_params(attributes)
382         self.content.update(attributes)
383         return self
384
385     def set_att(self, attr_name, attr_value):
386         self.content.update({attr_name: attr_value})
387         return self
388
389     def set_communication_protocol(self, proto_name):
390         self.protocol = proto_name
391         return self
392
393     def set_protocol_specific_parameters(self, proto_params):
394         self.proto_params = self._prepare_params(proto_params)
395         return self
396
397     def append_protocol_specific_parameters(self, proto_params):
398         if not proto_params:
399             return self
400         proto_params = self._prepare_params(proto_params)
401         self.proto_params.update(proto_params)
402         return self
403
404     def set_proto_param(self, param_name, param_value):
405         self.proto_params.update({param_name: param_value})
406         return self
407
408     def clone(self):
409         raise NotImplementedError()
410
411     def build(self):
412         return OneM2MJsonPrimitive(
413             self.parameters, self.content, self.protocol, self.proto_params
414         )