Repository: iotile/python_iotile_cloud

Issue:
KeyError on streamer
It seems this should be `last_id` instead of `highest_id`. @timburke can you confirm?
https://github.com/iotile/python_iotile_cloud/blob/4cd309944c3702a252e953cfcf199994c80aeab1/iotile_cloud/utils/mock_cloud.py#L266

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fd79bda..8a6b6a6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,8 @@
-### 0.8.4
+### v0.8.5 (2018-02-23)
+
+  * Fix get stream acknowledgment to get the right dictionary field (Issue #28)
+  
+### v0.8.4
 
   * Add support for PATCH in API
 
diff --git a/iotile_cloud/utils/mock_cloud.py b/iotile_cloud/utils/mock_cloud.py
index 7196cb9..24be7f1 100644
--- a/iotile_cloud/utils/mock_cloud.py
+++ b/iotile_cloud/utils/mock_cloud.py
@@ -206,26 +206,56 @@ class MockIOTileCloud(object):
             raise JSONErrorCode({'error': 'missing timestamp argument'}, 400)
 
         infile = request.files['file']
+        self.logger.info("Received uploaded report, filename: %s", infile.filename)
+
         indata = infile.read()
 
-        if len(indata) < (20 + 16):
-            print("Invalid input data length, too short, length=%d" % len(indata))
-            raise ErrorCode({'error': 'invalid file length'}, 400)
+        # Quick code to support both message pack reports and SignedListReport without depending on ReportParser
+        if infile.filename.endswith(".mp"):
+            try:
+                import msgpack
+            except ImportError:
+                self.logger.error("python-msgpack not installed, cannot parse message pack report")
+                return {'count': 0}
+
+            report = msgpack.unpackb(indata)
+
+            fmt = report.get('format')
+
+            lowest_id = report.get('lowest_id', 0)
+            highest_id = report.get('highest_id', 0)
+            device_id = report.get('device', 0)
+            origin_streamer = report.get('streamer_index')
+            streamer_selector = report.get('streamer_selector')
+            sent_timestamp = report.get('device_sent_timestamp')
+            report_id = report.get('incremental_id')
+            count = len(report.get('data', []))
+
+            if fmt != 'v100':
+                self.logger.error("Invalid format given in message packed report: %s", fmt)
+                raise ErrorCode(400)
+        else:
+            if len(indata) < (20 + 16):
+                print("Invalid input data length, too short, length=%d" % len(indata))
+                raise ErrorCode({'error': 'invalid file length'}, 400)
 
-        inheader = indata[:20]
-        infooter = indata[-24:]
+            inheader = indata[:20]
+            infooter = indata[-24:]
 
-        lowest_id, highest_id = struct.unpack_from("<LL", infooter)
-        fmt, len_low, len_high, device_id, report_id, sent_timestamp, _signature_flags, origin_streamer, streamer_selector = struct.unpack("<BBHLLLBBH", inheader)
+            lowest_id, highest_id = struct.unpack_from("<LL", infooter)
+            fmt, len_low, len_high, device_id, report_id, sent_timestamp, _signature_flags, origin_streamer, streamer_selector = struct.unpack("<BBHLLLBBH", inheader)
 
-        length = (len_high << 8) | len_low
-        if length != len(indata):
-            print("Invalid input data length, did not match, expected: %d, found: %d" % (length, len(indata)))
-            raise ErrorCode(500)
+            length = (len_high << 8) | len_low
+            if length != len(indata):
+                print("Invalid input data length, did not match, expected: %d, found: %d" % (length, len(indata)))
+                raise ErrorCode(500)
+
+            if fmt != 1:
+                print("Invalid report format code, expected: %d, found: %d" % (1, fmt))
+                raise ErrorCode(500)
+
+            count = (length - 24 - 20) // 16
 
-        if fmt != 1:
-            print("Invalid report format code, expected: %d, found: %d" % (1, fmt))
-            raise ErrorCode(500)
 
         old_highest = self._get_streamer_ack(device_id, origin_streamer)
         if highest_id > old_highest:
@@ -237,7 +267,7 @@ class MockIOTileCloud(object):
             act_first = None
             act_last = None
         elif act_first <= highest_id:
-            act_first = highest_id + 1
+            act_first = highest_id + 1            
 
         report_record = {
             "id": str(uuid.uuid4()),
@@ -257,13 +287,13 @@ class MockIOTileCloud(object):
         self.reports[report_record['id']] = report_record
         self.raw_report_files[report_record['id']] = indata
 
-        return {'count': (length - 24 - 20) // 16}
+        return {'count': count}
 
     def _get_streamer_ack(self, device_id, index):
         streamer = str(gid.IOTileStreamerSlug(device_id, index))
 
         if streamer in self.streamers:
-            return self.streamers[streamer]['highest_id']
+            return self.streamers[streamer]['last_id']
 
         return 0
 
@@ -799,6 +829,9 @@ class MockIOTileCloud(object):
             selector (int): Optional selector criteria used by this streamer.  If this
                 is specified it is used as is.  If not, the default selector for each
                 index is used.
+
+        Returns:
+            str: The slug of the streamer that was just created
         """
 
         default_selectors = {
@@ -832,6 +865,8 @@ class MockIOTileCloud(object):
 
         self.streamers[streamer_slug] = streamer_data
 
+        return streamer_slug
+
     def quick_add_fleet(self, devices, is_network=False, fleet_slug=None, org_slug=None):
         """Quickly add a fleet.
 
diff --git a/version.py b/version.py
index 63fa052..ef88a31 100644
--- a/version.py
+++ b/version.py
@@ -1,1 +1,1 @@
-version = '0.8.4'
+version = '0.8.5'


Test patch:
diff --git a/tests/test_mock_cloud.py b/tests/test_mock_cloud.py
index f537d5c..4297095 100644
--- a/tests/test_mock_cloud.py
+++ b/tests/test_mock_cloud.py
@@ -138,6 +138,12 @@ def test_quick_add_functionality(mock_cloud_private_nossl):
     org_data = api.org(proj_data['org']).get()
     assert org_data['slug'] == "quick-test-org"
 
+    # Make sure quick add streamer works (Issue 28)
+    slug = cloud.quick_add_streamer(1, 0, 10)
+    resp = api.streamer(slug).get()
+    print(resp)
+    assert resp['last_id'] == 10
+
 
 def test_quick_add_device(mock_cloud_private_nossl):
     """Make sure quick_add_device works."""