kinto 20.2.0__py3-none-any.whl → 20.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kinto might be problematic. Click here for more details.
- kinto/core/storage/memory.py +16 -3
- kinto/core/storage/postgresql/__init__.py +1 -1
- kinto/core/storage/postgresql/migrations/migration_022_023.sql +5 -0
- kinto/core/storage/postgresql/schema.sql +3 -2
- kinto/core/storage/testing.py +8 -1
- kinto/plugins/history/listener.py +60 -5
- {kinto-20.2.0.dist-info → kinto-20.3.0.dist-info}/METADATA +1 -1
- {kinto-20.2.0.dist-info → kinto-20.3.0.dist-info}/RECORD +12 -11
- {kinto-20.2.0.dist-info → kinto-20.3.0.dist-info}/WHEEL +0 -0
- {kinto-20.2.0.dist-info → kinto-20.3.0.dist-info}/entry_points.txt +0 -0
- {kinto-20.2.0.dist-info → kinto-20.3.0.dist-info}/licenses/LICENSE +0 -0
- {kinto-20.2.0.dist-info → kinto-20.3.0.dist-info}/top_level.txt +0 -0
kinto/core/storage/memory.py
CHANGED
|
@@ -288,13 +288,26 @@ class Storage(MemoryBasedStorage):
|
|
|
288
288
|
modified_field=DEFAULT_MODIFIED_FIELD,
|
|
289
289
|
):
|
|
290
290
|
parent_id_match = re.compile(parent_id.replace("*", ".*"))
|
|
291
|
-
|
|
291
|
+
|
|
292
|
+
timestamps_by_parent_id = {
|
|
292
293
|
pid: resources
|
|
293
|
-
for pid, resources in self.
|
|
294
|
+
for pid, resources in self._timestamps.items()
|
|
294
295
|
if parent_id_match.match(pid)
|
|
295
296
|
}
|
|
297
|
+
if resource_name is not None:
|
|
298
|
+
for pid, resources in timestamps_by_parent_id.items():
|
|
299
|
+
del self._timestamps[pid][resource_name]
|
|
300
|
+
else:
|
|
301
|
+
for pid, resources in timestamps_by_parent_id.items():
|
|
302
|
+
del self._timestamps[pid]
|
|
303
|
+
|
|
296
304
|
num_deleted = 0
|
|
297
|
-
|
|
305
|
+
tombstones_by_parent_id = {
|
|
306
|
+
pid: resources
|
|
307
|
+
for pid, resources in self._cemetery.items()
|
|
308
|
+
if parent_id_match.match(pid)
|
|
309
|
+
}
|
|
310
|
+
for pid, resources in tombstones_by_parent_id.items():
|
|
298
311
|
if resource_name is not None:
|
|
299
312
|
resources = {resource_name: resources[resource_name]}
|
|
300
313
|
for resource, resource_objects in resources.items():
|
|
@@ -47,7 +47,8 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_objects_parent_id_resource_name_last_modif
|
|
|
47
47
|
ON objects(parent_id, resource_name, last_modified DESC);
|
|
48
48
|
CREATE INDEX IF NOT EXISTS idx_objects_last_modified_epoch
|
|
49
49
|
ON objects(as_epoch(last_modified));
|
|
50
|
-
|
|
50
|
+
CREATE INDEX IF NOT EXISTS idx_objects_resource_name_parent_id_deleted
|
|
51
|
+
ON objects(resource_name, parent_id, deleted);
|
|
51
52
|
|
|
52
53
|
CREATE TABLE IF NOT EXISTS timestamps (
|
|
53
54
|
parent_id TEXT NOT NULL COLLATE "C",
|
|
@@ -131,4 +132,4 @@ INSERT INTO metadata (name, value) VALUES ('created_at', NOW()::TEXT);
|
|
|
131
132
|
|
|
132
133
|
-- Set storage schema version.
|
|
133
134
|
-- Should match ``kinto.core.storage.postgresql.PostgreSQL.schema_version``
|
|
134
|
-
INSERT INTO metadata (name, value) VALUES ('storage_schema_version', '
|
|
135
|
+
INSERT INTO metadata (name, value) VALUES ('storage_schema_version', '23');
|
kinto/core/storage/testing.py
CHANGED
|
@@ -1296,6 +1296,9 @@ class DeletedObjectsTest:
|
|
|
1296
1296
|
self.create_object(parent_id="/abc/a", resource_name="c")
|
|
1297
1297
|
self.create_object(parent_id="/efg", resource_name="c")
|
|
1298
1298
|
|
|
1299
|
+
all_timestamps = self.storage.all_resources_timestamps(resource_name="c")
|
|
1300
|
+
self.assertEqual(set(all_timestamps.keys()), {"/abc/a", "/efg"})
|
|
1301
|
+
|
|
1299
1302
|
before1 = self.storage.resource_timestamp(parent_id="/abc/a", resource_name="c")
|
|
1300
1303
|
# Different parent_id with object.
|
|
1301
1304
|
before2 = self.storage.resource_timestamp(parent_id="/efg", resource_name="c")
|
|
@@ -1305,11 +1308,15 @@ class DeletedObjectsTest:
|
|
|
1305
1308
|
self.storage.delete_all(parent_id="/abc/*", resource_name=None, with_deleted=False)
|
|
1306
1309
|
self.storage.purge_deleted(parent_id="/abc/*", resource_name=None)
|
|
1307
1310
|
|
|
1311
|
+
all_timestamps = self.storage.all_resources_timestamps(resource_name="c")
|
|
1312
|
+
self.assertEqual(set(all_timestamps.keys()), {"/efg", "/ijk"})
|
|
1313
|
+
|
|
1314
|
+
time.sleep(0.002) # make sure we don't recreate timestamps at same msec.
|
|
1308
1315
|
after1 = self.storage.resource_timestamp(parent_id="/abc/a", resource_name="c")
|
|
1309
1316
|
after2 = self.storage.resource_timestamp(parent_id="/efg", resource_name="c")
|
|
1310
1317
|
after3 = self.storage.resource_timestamp(parent_id="/ijk", resource_name="c")
|
|
1311
1318
|
|
|
1312
|
-
self.assertNotEqual(before1, after1)
|
|
1319
|
+
self.assertNotEqual(before1, after1) # timestamp was removed, it will differ.
|
|
1313
1320
|
self.assertEqual(before2, after2)
|
|
1314
1321
|
self.assertEqual(before3, after3)
|
|
1315
1322
|
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from datetime import datetime, timezone
|
|
2
3
|
|
|
3
4
|
from pyramid.settings import asbool, aslist
|
|
4
5
|
|
|
5
|
-
from kinto.core.
|
|
6
|
+
from kinto.core.storage import Filter, Sort
|
|
7
|
+
from kinto.core.utils import COMPARISON, instance_uri
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
6
11
|
|
|
7
12
|
|
|
8
13
|
def on_resource_changed(event):
|
|
@@ -14,15 +19,26 @@ def on_resource_changed(event):
|
|
|
14
19
|
payload = event.payload
|
|
15
20
|
resource_name = payload["resource_name"]
|
|
16
21
|
event_uri = payload["uri"]
|
|
17
|
-
|
|
18
|
-
bucket_id = None
|
|
19
|
-
bucket_uri = None
|
|
20
|
-
collection_uri = None
|
|
22
|
+
user_id = payload["user_id"]
|
|
21
23
|
|
|
22
24
|
storage = event.request.registry.storage
|
|
23
25
|
permission = event.request.registry.permission
|
|
24
26
|
settings = event.request.registry.settings
|
|
25
27
|
|
|
28
|
+
excluded_user_ids = aslist(settings.get("history.exclude_user_ids", ""))
|
|
29
|
+
if user_id in excluded_user_ids:
|
|
30
|
+
logger.info(f"History entries for user {user_id!r} are disabled in config")
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
trim_history_max = int(settings.get("history.auto_trim_max_count", "-1"))
|
|
34
|
+
is_trim_enabled = trim_history_max > 0
|
|
35
|
+
trim_user_ids = aslist(settings.get("history.auto_trim_user_ids", ""))
|
|
36
|
+
is_trim_by_user_enabled = len(trim_user_ids) > 0
|
|
37
|
+
|
|
38
|
+
bucket_id = None
|
|
39
|
+
bucket_uri = None
|
|
40
|
+
collection_uri = None
|
|
41
|
+
|
|
26
42
|
excluded_resources = aslist(settings.get("history.exclude_resources", ""))
|
|
27
43
|
|
|
28
44
|
targets = []
|
|
@@ -38,6 +54,7 @@ def on_resource_changed(event):
|
|
|
38
54
|
bucket_uri = instance_uri(event.request, "bucket", id=bucket_id)
|
|
39
55
|
|
|
40
56
|
if bucket_uri in excluded_resources:
|
|
57
|
+
logger.info(f"History entries for bucket {bucket_uri!r} are disabled in config")
|
|
41
58
|
continue
|
|
42
59
|
|
|
43
60
|
if "collection_id" in payload:
|
|
@@ -46,6 +63,9 @@ def on_resource_changed(event):
|
|
|
46
63
|
event.request, "collection", bucket_id=bucket_id, id=collection_id
|
|
47
64
|
)
|
|
48
65
|
if collection_uri in excluded_resources:
|
|
66
|
+
logger.info(
|
|
67
|
+
f"History entries for collection {collection_uri!r} are disabled in config"
|
|
68
|
+
)
|
|
49
69
|
continue
|
|
50
70
|
|
|
51
71
|
# On POST .../records, the URI does not contain the newly created
|
|
@@ -59,6 +79,7 @@ def on_resource_changed(event):
|
|
|
59
79
|
uri = "/".join(parts)
|
|
60
80
|
|
|
61
81
|
if uri in excluded_resources:
|
|
82
|
+
logger.info(f"History entries for record {uri!r} are disabled in config")
|
|
62
83
|
continue
|
|
63
84
|
|
|
64
85
|
targets.append((uri, target))
|
|
@@ -109,6 +130,40 @@ def on_resource_changed(event):
|
|
|
109
130
|
# Note: this will be rolledback if the transaction is rolledback.
|
|
110
131
|
entry = storage.create(parent_id=bucket_uri, resource_name="history", obj=attrs)
|
|
111
132
|
|
|
133
|
+
# If enabled, we trim history by resource.
|
|
134
|
+
# This means that we will only keep the last `auto_trim_max_count` history entries
|
|
135
|
+
# for this same type of object (eg. `collection`, `record`).
|
|
136
|
+
#
|
|
137
|
+
# If trim by user is enabled, we only trim if the user matches the config
|
|
138
|
+
# and we only delete the history entries of this user.
|
|
139
|
+
# This means that if a user touches X different types of objects, we will keep
|
|
140
|
+
# ``(X * auto_trim_max_count)`` entries.
|
|
141
|
+
if is_trim_enabled and (not is_trim_by_user_enabled or user_id in trim_user_ids):
|
|
142
|
+
filters = [
|
|
143
|
+
Filter("resource_name", resource_name, COMPARISON.EQ),
|
|
144
|
+
]
|
|
145
|
+
if is_trim_by_user_enabled:
|
|
146
|
+
filters.append(Filter("user_id", user_id, COMPARISON.EQ))
|
|
147
|
+
|
|
148
|
+
# Identify the oldest entry to keep.
|
|
149
|
+
previous_entries = storage.list_all(
|
|
150
|
+
parent_id=bucket_uri,
|
|
151
|
+
resource_name="history",
|
|
152
|
+
filters=filters,
|
|
153
|
+
sorting=[Sort("last_modified", -1)],
|
|
154
|
+
limit=trim_history_max + 1,
|
|
155
|
+
)
|
|
156
|
+
# And delete all older ones.
|
|
157
|
+
if len(previous_entries) > trim_history_max:
|
|
158
|
+
trim_before_timestamp = previous_entries[-1]["last_modified"]
|
|
159
|
+
deleted = storage.delete_all(
|
|
160
|
+
parent_id=bucket_uri,
|
|
161
|
+
resource_name="history",
|
|
162
|
+
filters=filters
|
|
163
|
+
+ [Filter("last_modified", trim_before_timestamp, COMPARISON.MAX)],
|
|
164
|
+
)
|
|
165
|
+
logger.debug(f"Trimmed {len(deleted)} old history entries.")
|
|
166
|
+
|
|
112
167
|
# Without explicit permissions, the ACLs on the history entries will
|
|
113
168
|
# fully depend on the inherited permission tree (eg. bucket:read, bucket:write).
|
|
114
169
|
# This basically means that if user loose the permissions on the related
|
|
@@ -61,14 +61,14 @@ kinto/core/resource/viewset.py,sha256=Wo7mQwmI08IGnSetaqGF66fCqYPB1pDUdZa3U92NIi
|
|
|
61
61
|
kinto/core/storage/__init__.py,sha256=Bo9q5PCDQ7KkBuBAHt7UoZFr5WfVWwEbFJjGsuq4Oo4,13704
|
|
62
62
|
kinto/core/storage/exceptions.py,sha256=o10f7LohwyCHOTlR-dOdnB4us_MdCGOJUxZO8HZ3Akc,1304
|
|
63
63
|
kinto/core/storage/generators.py,sha256=rxWN9hOfOsB-PLeryhPGO-aE670sivr3LFRIExImpxc,1829
|
|
64
|
-
kinto/core/storage/memory.py,sha256=
|
|
65
|
-
kinto/core/storage/testing.py,sha256=
|
|
64
|
+
kinto/core/storage/memory.py,sha256=DR6gpqSYh4oGPBX9x4-dGLNITZi33T0VODsNWsPtgho,19875
|
|
65
|
+
kinto/core/storage/testing.py,sha256=4hGR6xc54M6H81IQb8XP7NP_iBP-X8m8PqKqixDs5nU,78411
|
|
66
66
|
kinto/core/storage/utils.py,sha256=BHpohIKVOCtURjRbUT7O5AEhIKfSEFv-pfgRzq8Q5zs,1082
|
|
67
|
-
kinto/core/storage/postgresql/__init__.py,sha256=
|
|
67
|
+
kinto/core/storage/postgresql/__init__.py,sha256=HMlUfTBsnsUQkLEhuhItU-9fgSOf9wTH6J09-1HW1fA,40157
|
|
68
68
|
kinto/core/storage/postgresql/client.py,sha256=JTRxHK-8ipTXgs0E4PyiFiun-32yuzqFGsWTi-OuFfA,4270
|
|
69
69
|
kinto/core/storage/postgresql/migrator.py,sha256=MQ_5aSrDi9-s2wlyyFyfhYP6HreCXjtlJzBI4b85u1I,3524
|
|
70
70
|
kinto/core/storage/postgresql/pool.py,sha256=lOtclVagFqzzWbVxrGoWeKylpHlKdFgGz3Ef6cgGNJU,2219
|
|
71
|
-
kinto/core/storage/postgresql/schema.sql,sha256=
|
|
71
|
+
kinto/core/storage/postgresql/schema.sql,sha256=qRmx8NSwqedwozOi-Nn-pnipubxUWLCcYTRDMkt2B80,4216
|
|
72
72
|
kinto/core/storage/postgresql/migrations/migration_001_002.sql,sha256=GVJIs8MGmZEyp1i0KjsmGKv1pLlBRckn0EWX6Kl6uQE,428
|
|
73
73
|
kinto/core/storage/postgresql/migrations/migration_002_003.sql,sha256=zlSZpG_2L-wd8KXh3szmbYtoGjAOOwy2gH7RFMUd61w,1203
|
|
74
74
|
kinto/core/storage/postgresql/migrations/migration_003_004.sql,sha256=OZSbH6Yt1PA2zba8iQWIguaTsnn3v7bFF-rbg2_teXY,530
|
|
@@ -90,6 +90,7 @@ kinto/core/storage/postgresql/migrations/migration_018_019.sql,sha256=XK6ex2jwQQ
|
|
|
90
90
|
kinto/core/storage/postgresql/migrations/migration_019_020.sql,sha256=yDQDdzU65cgctKIeo1YqMrFi7aU2VGdBhVUpVQAQOgM,306
|
|
91
91
|
kinto/core/storage/postgresql/migrations/migration_020_021.sql,sha256=cEgfGNDLH3RyLswxy9YZazZtOvidxNsi57z7SGR8VsQ,2369
|
|
92
92
|
kinto/core/storage/postgresql/migrations/migration_021_022.sql,sha256=MUIAfgbBDQLy8JfklEs6ekK93nr1buVRtmBAHaaMXug,1993
|
|
93
|
+
kinto/core/storage/postgresql/migrations/migration_022_023.sql,sha256=c1Fw10sxtaDqYJpxk1P9eZCtXP3uSmBY7-3IjZZLwjU,231
|
|
93
94
|
kinto/core/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
94
95
|
kinto/core/views/batch.py,sha256=sWNn67YqfTbiqwpbwbm1QyumcVGi8aNhlT5AtLToElI,5657
|
|
95
96
|
kinto/core/views/errors.py,sha256=2uTy85UBQL1EDIMotzFBhVl14RmWwb3rupbsB0mycbs,6099
|
|
@@ -127,7 +128,7 @@ kinto/plugins/admin/build/assets/ttcn-cfg-B9xdYoR4.js,sha256=jTZ_XHKOChJZEt-FzJb
|
|
|
127
128
|
kinto/plugins/admin/public/help.html,sha256=1hol7z5Sv0Xn3mYyEfPQWFOsrR24htlKhmnGA3rH8fs,958
|
|
128
129
|
kinto/plugins/default_bucket/__init__.py,sha256=7TmBzFgW0gmYsetXr6Kqt9317XZ3PiB9gyCr-IBfmGg,7276
|
|
129
130
|
kinto/plugins/history/__init__.py,sha256=s-RMNWaZlmBNd4sNsgJ-hDvMW4pPKUZ-sdZYubb0Kdo,1015
|
|
130
|
-
kinto/plugins/history/listener.py,sha256=
|
|
131
|
+
kinto/plugins/history/listener.py,sha256=WuMky5f3Ek6NaRQODTO-kLSD9mftJwCvxHeQ7Vy4_JA,7628
|
|
131
132
|
kinto/plugins/history/views.py,sha256=NoBP-S7epeH5TLZZbIqfBmwMA2KaWmxP7lqPAS11BTU,2293
|
|
132
133
|
kinto/plugins/openid/__init__.py,sha256=1Iv5SCa6vwEvoJkmGd45-TYm_mxz2okFj6u2VTNuVrk,4863
|
|
133
134
|
kinto/plugins/openid/utils.py,sha256=n3KGS-ogXR2sg6j4QtdPe_DtEsqD7AGVT2K7vhl8yE8,273
|
|
@@ -140,9 +141,9 @@ kinto/views/contribute.py,sha256=PJoIMLj9_IszSjgZkaCd_TUjekDgNqjpmVTmRN9ztaA,983
|
|
|
140
141
|
kinto/views/groups.py,sha256=jOq5fX0-4lwZE8k1q5HME2tU7x9052rtBPF7YqcJ-Qg,3181
|
|
141
142
|
kinto/views/permissions.py,sha256=F0_eKx201WyLonXJ5vLdGKa9RcFKjvAihrEEhU1JuLw,9069
|
|
142
143
|
kinto/views/records.py,sha256=lYfACW2L8qcQoyYBD5IX-fTPjFWmGp7GjHq_U4InlyE,5037
|
|
143
|
-
kinto-20.
|
|
144
|
-
kinto-20.
|
|
145
|
-
kinto-20.
|
|
146
|
-
kinto-20.
|
|
147
|
-
kinto-20.
|
|
148
|
-
kinto-20.
|
|
144
|
+
kinto-20.3.0.dist-info/licenses/LICENSE,sha256=oNEIMTuTJzppR5ZEyi86yvvtSagveMYXTYFn56zF0Uk,561
|
|
145
|
+
kinto-20.3.0.dist-info/METADATA,sha256=WFcv90kV4fQrRr1nR8GQknnmY6Zo9_n1wk1Z-M7nYXg,8731
|
|
146
|
+
kinto-20.3.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
147
|
+
kinto-20.3.0.dist-info/entry_points.txt,sha256=3KlqBWPKY81mrCe_oX0I5s1cRO7Q53nCLbnVr5P9LH4,85
|
|
148
|
+
kinto-20.3.0.dist-info/top_level.txt,sha256=EG_YmbZL6FAug9VwopG7JtF9SvH_r0DEnFp-3twPPys,6
|
|
149
|
+
kinto-20.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|