Resolve PEP8 in IoTDM
[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__(self, parameters, content,
36                  protocol_name, protocol_parameters, short_scheme=True):
37         self.parameters = parameters
38         self.content = content
39         self.protocol = protocol_name
40         self.proto_params = protocol_parameters
41         self.short_scheme = short_scheme
42
43     def get_parameters(self):
44         return self.parameters
45
46     def get_parameters_str(self):
47         return json.dumps(self.parameters)
48
49     def _create_json_pointer(self, pointer_string):
50         try:
51             json_pointer = str(pointer_string)
52             # add leading slash if missing
53             if json_pointer[0] != '/':
54                 json_pointer = '/' + json_pointer
55
56             # remove slash from the end if exists
57             if json_pointer[-1] == '/':
58                 json_pointer = json_pointer[:-1]
59
60             json_pointer = JsonPointer(json_pointer)
61         except Exception as e:
62             raise RuntimeError("Invalid JSON pointer passed: {}, error: {}".format(pointer_string, e.message))
63         return json_pointer
64
65     def _get_item_by_pointer(self, data_dict, pointer):
66         if None is data_dict:
67             raise AttributeError("No JSON data passed")
68
69         if not isinstance(pointer, JsonPointer):
70             json_pointer = self._create_json_pointer(pointer)
71         else:
72             json_pointer = pointer
73
74         try:
75             item = json_pointer.resolve(data_dict)
76         except JsonPointerException as e:
77             raise RuntimeError("Failed to get JSON item by JSON pointer: {}, error: {}".format(pointer, e.message))
78
79         return item
80
81     def _has_item_by_pointer(self, data_dict, pointer):
82         if None is data_dict:
83             raise AttributeError("No JSON data passed")
84
85         if not isinstance(pointer, JsonPointer):
86             json_pointer = self._create_json_pointer(pointer)
87         else:
88             json_pointer = pointer
89
90         try:
91             json_pointer.resolve(data_dict)
92         except JsonPointerException:
93             return False
94
95         return True
96
97     def get_param(self, param):
98         """Returns container or item value identified by string or JsonPointer object"""
99         return self._get_item_by_pointer(self.parameters, param)
100
101     def has_param(self, param):
102         """Returns True if parameter identified by string or JsonPointer object exists, False otherwise"""
103         return self._has_item_by_pointer(self.parameters, param)
104
105     def get_content(self):
106         return self.content
107
108     def get_content_str(self):
109         if not self.content:
110             return ""
111         return json.dumps(self.content)
112
113     def get_attr(self, attr):
114         """Returns container or item value identified by string or JsonPointer object"""
115         return self._get_item_by_pointer(self.content, attr)
116
117     def has_attr(self, attr):
118         """Returns True if attribute identified by string or JsonPointer object exists, False otherwise"""
119         return self._has_item_by_pointer(self.content, attr)
120
121     def get_protocol_specific_parameters(self):
122         return self.proto_params
123
124     def get_protocol_specific_parameters_str(self):
125         return json.dumps(self.proto_params)
126
127     def get_proto_param(self, proto_param):
128         """Returns container or item value identified by string or JsonPointer object"""
129         return self._get_item_by_pointer(self.proto_params, proto_param)
130
131     def has_proto_param(self, proto_param):
132         """Returns True if parameter identified by string or JsonPointer object exists, False otherwise"""
133         return self._has_item_by_pointer(self.proto_params, proto_param)
134
135     def get_primitive_str(self):
136         """
137         Returns whole OneM2M primitive as JSON string including primitive
138         parameters and primitive content
139         """
140         primitive = {}
141         if self.parameters:
142             primitive = self.parameters.copy()
143
144         if self.content:
145             primitive[OneM2M.short_primitive_content] = self.content.copy()
146
147         return json.dumps(primitive)
148
149     def get_communication_protocol(self):
150         return self.protocol
151
152     def _check_protocol_of_request(self):
153         if not self.get_communication_protocol():
154             raise AssertionError("Communication protocol of request primitive not set")
155
156     def _check_protocol_of_response(self, response_primitive):
157         if not response_primitive.get_communication_protocol():
158             raise AssertionError("Communication protocol of response primitive not set")
159
160     def _check_exchange_protocols(self, response_primitive):
161         self._check_protocol_of_request()
162         self._check_protocol_of_response(response_primitive)
163         if not self.get_communication_protocol() == response_primitive.get_communication_protocol():
164             raise AssertionError("Request {} and response {} primitives' communication protocols doesn't match.".
165                                  format(self.get_communication_protocol(),
166                                         response_primitive.get_communication_protocol()))
167
168     def _check_request_common(self):
169         op = self.get_param(OneM2M.short_operation)
170         if not op:
171             raise AssertionError("Request primitive without operation set")
172
173         if not isinstance(op, int):
174             raise AssertionError("Invalid data type ({}) of operation where integer is expected".format(op.__class__))
175
176         if op not in OneM2M.operation_valid_values:
177             raise AssertionError("Request primitive with unknown operation set: {}".format(op))
178
179         rqi = self.get_param(OneM2M.short_request_identifier)
180         if not rqi:
181             raise AssertionError("Request primitive without request id")
182
183         if not isinstance(rqi, basestring):
184             raise AssertionError("Invalid data type ({}) of request identifier where string is expected".
185                                  format(rqi.__class__))
186         return op, rqi
187
188     def _check_response_common(self, response_primitive, rqi=None, rsc=None):
189         rsp_rqi = response_primitive.get_param(OneM2M.short_request_identifier)
190         if not rsp_rqi:
191             raise AssertionError("Response primitive without request id")
192
193         if not isinstance(rsp_rqi, basestring):
194             raise AssertionError("Invalid data type ({}) of request identifier where string is expected".
195                                  format(rsp_rqi.__class__))
196
197         if rqi and rqi != rsp_rqi:
198             raise AssertionError("Request IDs mismatch: req: {}, rsp: {}".format(rqi, rsp_rqi))
199
200         r_rsc = response_primitive.get_param(OneM2M.short_response_status_code)
201         if not r_rsc:
202             raise AssertionError("Response primitive without status code")
203
204         if not isinstance(r_rsc, int):
205             raise AssertionError("Invalid data type ({}) of response status code where integer is expected".
206                                  format(r_rsc.__class__))
207
208         if r_rsc not in OneM2M.supported_result_codes:
209             raise AssertionError("Unsupported response primitive result code: {}".format(r_rsc))
210
211         if None is not rsc:
212             if r_rsc != rsc:
213                 raise AssertionError("Unexpected result code: {}, expected: {}".format(r_rsc, rsc))
214
215         return r_rsc
216
217     def _check_exchange_common(self, response_primitive, rsc=None):
218         self._check_exchange_protocols(response_primitive)
219         op, rqi = self._check_request_common()
220         r_rsc = self._check_response_common(response_primitive, rqi, rsc)
221         return op, r_rsc
222
223     def _check_response_positive_result(self, response_rsc=None, request_operation=None):
224         if response_rsc and response_rsc not in OneM2M.positive_result_codes:
225             raise AssertionError("Response with negative status code: {}".format(response_rsc))
226
227         if None is request_operation:
228             return
229
230         expected_rsc = OneM2M.expected_result_codes[request_operation]
231         if expected_rsc != response_rsc:
232             raise AssertionError("Unexpected positive result code for operation: {}, received: {}, expected: {}".format(
233                                  request_operation, response_rsc, expected_rsc))
234
235     def check_exchange(self, response_primitive, rsc=None):
236         op, r_rsc = self._check_exchange_common(response_primitive, rsc)
237         self._check_response_positive_result(r_rsc, op)
238
239     def _check_response_negative_result(self, response_primitive, error_message):
240         if not response_primitive:
241             raise AttributeError("Response primitive not passed")
242
243         if not error_message:
244             return
245
246         msg = response_primitive.get_attr(OneM2M.error_message_item)
247         if not msg:
248             raise AssertionError("Negative response primitive without error message, expected message: {}".format(
249                                  error_message))
250
251         if not isinstance(msg, basestring):
252             raise AssertionError("Invalid data type ({}) of response error message where string is expected".
253                                  format(msg.__class__))
254
255         if not msg == error_message:
256             raise AssertionError("Negative response with unexpected error message: {}, expected: {}".format(
257                                  msg, error_message))
258
259     def check_exchange_negative(self, response_primitive, rsc, error_message=None):
260         op, r_rsc = self._check_exchange_common(response_primitive, rsc)
261         self._check_response_negative_result(response_primitive, error_message)
262
263     def check_request(self):
264         self._check_protocol_of_request()
265         self._check_request_common()
266
267     def check_response(self, rqi=None, rsc=None, request_operation=None):
268         self._check_protocol_of_response(self)
269         self._check_response_common(self, rqi, rsc)
270         self._check_response_positive_result(rsc, request_operation)
271
272     def check_response_negative(self, rqi=None, rsc=None, error_message=None):
273         self._check_protocol_of_response(self)
274         self._check_response_common(self, rqi, rsc)
275         self._check_response_negative_result(self, error_message)
276
277     def _compare(self, primitive2):
278         raise NotImplementedError()
279
280
281 class OneM2MJsonPrimitiveBuilder(OneM2MPrimitiveBuilder, OneM2MJsonPrimitive):
282     """Generic implementation of builder class for OneM2M JSON primitives"""
283
284     def __init__(self):
285         self.parameters = {}
286         self.content = {}
287         self.protocol = None
288         self.proto_params = {}
289         self.short_scheme = None
290
291     def _prepare_params(self, params):
292         if not params:
293             return {}
294
295         if isinstance(params, unicode):
296             params = str(params)
297
298         if isinstance(params, basestring):
299             params = json.loads(params)
300             return params
301
302         if isinstance(params, dict):
303             return params.copy()
304
305         raise OneM2MPrimitiveBuilderException("Unsupported parameters object type")
306
307     def set_parameters(self, parameters):
308         self.parameters = self._prepare_params(parameters)
309         return self
310
311     def append_parameters(self, parameters):
312         if not parameters:
313             return self
314         parameters = self._prepare_params(parameters)
315         self.parameters.update(parameters)
316         return self
317
318     def set_param(self, param_name, param_value):
319         self.parameters.update({param_name: param_value})
320         return self
321
322     def set_content(self, attributes):
323         self.content = self._prepare_params(attributes)
324         return self
325
326     def append_content_attributes(self, attributes):
327         if not attributes:
328             return self
329         attributes = self._prepare_params(attributes)
330         self.content.update(attributes)
331         return self
332
333     def set_att(self, attr_name, attr_value):
334         self.content.update({attr_name: attr_value})
335         return self
336
337     def set_communication_protocol(self, proto_name):
338         self.protocol = proto_name
339         return self
340
341     def set_protocol_specific_parameters(self, proto_params):
342         self.proto_params = self._prepare_params(proto_params)
343         return self
344
345     def append_protocol_specific_parameters(self, proto_params):
346         if not proto_params:
347             return self
348         proto_params = self._prepare_params(proto_params)
349         self.proto_params.update(proto_params)
350         return self
351
352     def set_proto_param(self, param_name, param_value):
353         self.proto_params.update({param_name: param_value})
354         return self
355
356     def clone(self):
357         raise NotImplementedError()
358
359     def build(self):
360         return OneM2MJsonPrimitive(self.parameters,
361                                    self.content,
362                                    self.protocol,
363                                    self.proto_params)