Repository: Kinto/kinto

Issue:
History entry fields consistency
We reached a situation where we have `record_id` different than `data.target.id`. This should not be possible oO

```json
        {
            "action": "update",
            "collection_id": "gfx",
            "date": "2016-11-21T10:09:00.225241",
            "id": "ae7e6831-11ad-49da-a739-a6fc25a913e8",
            "last_modified": 1479722940230,
            "record_id": "5bbd4751-5616-a3cc-e31c-1d04ade888bf",
            "resource_name": "record",
            "target": {
                "data": {
                    "blockID": "g1239",
                    "details": {
                        "bug": "https://bugzilla.mozilla.org/show_bug.cgi?id=838845",
                        "created": "2016-10-18T09:29:16Z",
                        "name": "GFX1-Val",
                        "who": "test",
                        "why": "test"
                    },
                    "devices": [
                        "0x4396"
                    ],
                    "driverVersion": "6.1.7601.18328",
                    "driverVersionComparator": "LESS_THAN_OR_EQUAL",
                    "enabled": true,
                    "feature": "DIRECT3D_10_LAYERS",
                    "featureStatus": "BLOCKED_DRIVER_VERSION",
                    "id": "cf217981-2c42-76bf-df19-4324a194a075",
                    "last_modified": 1479722940206,
                    "os": "All",
                    "schema": 1478713951432,
                    "vendor": "0x1002"
                },
                "permissions": {
                    "write": [
                        "basicauth:3fa5a813e05061f219048d6b0d0a398ed99868213af0571886b76c2423dc097f"
                    ]
                }
            },
            "timestamp": 1478713951077,
            "uri": "/buckets/staging/collections/gfx/records/cf217981-2c42-76bf-df19-4324a194a075",
            "user_id": "basicauth:3fa5a813e05061f219048d6b0d0a398ed99868213af0571886b76c2423dc097f"
        },

```


Discussion:
What's present in `event.payload` is not totally consistent with the fact that we're doing a `BATCH` request contained 3 patch operations:

```
>>> event.payload

{ 'action': 'update',
  'bucket_id': u'staging',
  'collection_id': u'gfx',
  'record_id': u'fd6a9531-af10-79a0-e645-4249218dbf4f',
  'resource_name': 'record',
  'timestamp': 1479999631700L,
  'uri': u'/buckets/staging/collections/gfx/records/fd6a9531-af10-79a0-e645-4249218dbf4f',
  'user_id': 'basicauth:cbd3731f18c97ebe1d31d9846b5f1b95cf8eeeae586e201277263434041e99d1'}
```


```
>>> event.impacted_records

[ { 'new': { u'blockID': u'g1057',
             u'details': { u'bug': u'https://bugzilla.mozilla.org/show_bug.cgi?id=951422',
                           u'created': u'2015-11-18T12:52:47Z',
                           u'name': u'Intel driver < 6.14.10.5216 for DIRECT3D_9_LAYERS on XP',
                           u'who': u'.',
                           u'why': u'.'},
             u'devices': [],
             u'driverVersion': u'6.14.10.5218',
             u'driverVersionComparator': u'LESS_THAN',
             u'enabled': True,
             u'feature': u'WEBGL_ANGLE',
             u'featureStatus': u'BLOCKED_DRIVER_VERSION',
             'id': u'fd6a9531-af10-79a0-e645-4249218dbf4f',
             'last_modified': 1480064809393L,
             u'os': u'WINNT 5.1',
             u'schema': 1479999632223L,
             u'vendor': u'0x8086'},
    'old': { u'blockID': u'g1057',
             u'details': { u'bug': u'https://bugzilla.mozilla.org/show_bug.cgi?id=951422',
                           u'created': u'2015-11-18T12:52:47Z',
                           u'name': u'Intel driver < 6.14.10.5216 for DIRECT3D_9_LAYERS on XP',
                           u'who': u'.',
                           u'why': u'.'},
             u'devices': [],
             u'driverVersion': u'6.14.10.5218',
             u'driverVersionComparator': u'LESS_THAN',
             u'enabled': True,
             u'feature': u'WEBGL_ANGLE',
             u'featureStatus': u'BLOCKED_DRIVER_VERSION',
             'id': u'fd6a9531-af10-79a0-e645-4249218dbf4f',
             'last_modified': 1479999631630L,
             u'os': u'WINNT 5.1',
             u'schema': 1479999211622,
             u'vendor': u'0x8086'}},
  { 'new': { u'blockID': u'g1249',
             u'details': { u'bug': u'https://github.com/mozilla/addons-server/issues/3787',
                           u'created': u'2016-10-20T11:51:45Z',
                           u'name': u'GFX-blocklist-test',
                           u'who': u'test',
                           u'why': u'test'},
             u'devices': [u'0x9616'],
             u'driverVersion': u'8.723.0.0',
             u'driverVersionComparator': u'EQUAL',
             u'enabled': True,
             u'feature': u'DIRECT3D_11_LAYERS',
             u'featureStatus': u'BLOCKED_DRIVER_VERSION',
             'id': u'5bbd4751-5616-a3cc-e31c-1d04ade888bf',
             'last_modified': 1480064809430L,
             u'os': u'All',
             u'schema': 1479999632223L,
             u'vendor': u'0x1002'},
    'old': { u'blockID': u'g1249',
             u'details': { u'bug': u'https://github.com/mozilla/addons-server/issues/3787',
                           u'created': u'2016-10-20T11:51:45Z',
                           u'name': u'GFX-blocklist-test',
                           u'who': u'test',
                           u'why': u'test'},
             u'devices': [u'0x9616'],
             u'driverVersion': u'8.723.0.0',
             u'driverVersionComparator': u'EQUAL',
             u'enabled': True,
             u'feature': u'DIRECT3D_11_LAYERS',
             u'featureStatus': u'BLOCKED_DRIVER_VERSION',
             'id': u'5bbd4751-5616-a3cc-e31c-1d04ade888bf',
             'last_modified': 1479999631664L,
             u'os': u'All',
             u'schema': 1479999211622,
             u'vendor': u'0x1002'}},
  { 'new': { u'blockID': u'g1239',
             u'details': { u'bug': u'https://bugzilla.mozilla.org/show_bug.cgi?id=838845',
                           u'created': u'2016-10-18T09:29:16Z',
                           u'name': u'GFX1-Val',
                           u'who': u'test',
                           u'why': u'test'},
             u'devices': [u'0x4396'],
             u'driverVersion': u'6.1.7601.18328',
             u'driverVersionComparator': u'LESS_THAN_OR_EQUAL',
             u'enabled': True,
             u'feature': u'DIRECT3D_10_LAYERS',
             u'featureStatus': u'BLOCKED_DRIVER_VERSION',
             'id': u'cf217981-2c42-76bf-df19-4324a194a075',
             'last_modified': 1480064809477L,
             u'os': u'All',
             u'schema': 1479999632223L,
             u'vendor': u'0x1002'},
    'old': { u'blockID': u'g1239',
             u'details': { u'bug': u'https://bugzilla.mozilla.org/show_bug.cgi?id=838845',
                           u'created': u'2016-10-18T09:29:16Z',
                           u'name': u'GFX1-Val',
                           u'who': u'test',
                           u'why': u'test'},
             u'devices': [u'0x4396'],
             u'driverVersion': u'6.1.7601.18328',
             u'driverVersionComparator': u'LESS_THAN_OR_EQUAL',
             u'enabled': True,
             u'feature': u'DIRECT3D_10_LAYERS',
             u'featureStatus': u'BLOCKED_DRIVER_VERSION',
             'id': u'cf217981-2c42-76bf-df19-4324a194a075',
             'last_modified': 1479999631700L,
             u'os': u'All',
             u'schema': 1479999211622,
             u'vendor': u'0x1002'}}]
```

Resolving patch:
diff --git a/docs/conf.py b/docs/conf.py
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -74,7 +74,7 @@
 # The short X.Y version.
 version = '4.3'
 # The full version, including alpha/beta/rc tags.
-release = '4.3.5'
+release = '4.3.6'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
diff --git a/kinto/plugins/history/listener.py b/kinto/plugins/history/listener.py
--- a/kinto/plugins/history/listener.py
+++ b/kinto/plugins/history/listener.py
@@ -77,8 +77,9 @@ def on_resource_changed(event):
         # Prepare the history entry attributes.
         perms = {k: list(v) for k, v in perms_by_object_id[uri].items()}
         eventattrs = dict(**payload)
+        eventattrs.pop('timestamp', None)  # Already in target `last_modified`.
         eventattrs.pop('bucket_id', None)
-        eventattrs.setdefault('%s_id' % resource_name, obj_id)
+        eventattrs['%s_id' % resource_name] = obj_id
         eventattrs['uri'] = uri
         attrs = dict(date=datetime.now().isoformat(),
                      target={'data': target, 'permissions': perms},
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -84,7 +84,7 @@ def read_file(filename):
 
 
 setup(name='kinto',
-      version='4.3.6.dev0',
+      version='4.3.6',
       description='Kinto Web Service - Store, Sync, Share, and Self-Host.',
       long_description=README + "\n\n" + CHANGELOG + "\n\n" + CONTRIBUTORS,
       license='Apache License (2.0)',


Test patch:
diff --git a/tests/plugins/test_history.py b/tests/plugins/test_history.py
--- a/tests/plugins/test_history.py
+++ b/tests/plugins/test_history.py
@@ -481,6 +481,20 @@ def test_delete_on_collection(self):
         assert entries[1]['uri'] == '/buckets/bid/collections/cid/records/b'
         assert entries[2]['uri'] == '/buckets/bid/collections/cid/records/a'
 
+    def test_multiple_patch(self):
+        # Kinto/kinto#942
+        requests = [{
+            'method': 'PATCH',
+            'path': '/buckets/bid/collections/cid/records/%s' % l,
+            'body': {'data': {'label': l}}} for l in ('a', 'b', 'c')]
+        self.app.post_json('/batch', {'requests': requests}, headers=self.headers)
+        resp = self.app.get('/buckets/bid/history', headers=self.headers)
+        entries = resp.json['data']
+        for entry in entries:
+            if entry['resource_name'] != 'record':
+                continue
+            assert entry['record_id'] == entry['target']['data']['id']
+
 
 class DefaultBucketTest(HistoryWebTest):