Skip to content
This repository has been archived by the owner on Jan 25, 2022. It is now read-only.

Commit

Permalink
Merge fa11416 into 3fceab8
Browse files Browse the repository at this point in the history
  • Loading branch information
leplatrem committed Sep 22, 2020
2 parents 3fceab8 + fa11416 commit 5444d59
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 24 deletions.
67 changes: 43 additions & 24 deletions kinto_changes/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
from kinto.core.utils import instance_uri, COMPARISON

from .utils import monitored_collections, changes_object
from . import CHANGESET_PATH, CHANGES_RECORDS_PATH, MONITOR_BUCKET, CHANGES_COLLECTION
from . import (CHANGESET_PATH, CHANGES_RECORDS_PATH,
CHANGES_COLLECTION_PATH, MONITOR_BUCKET, CHANGES_COLLECTION)


class ChangesModel(object):
Expand Down Expand Up @@ -187,6 +188,13 @@ def __init__(self, request):
self.permission_object_id = collection_uri
self.required_permission = "read"

def check_permission(self, principals, bound_perms):
# The monitor/changes changeset endpoint is publicly accesible.
if self.permission_object_id == CHANGES_COLLECTION_PATH:
return True
# Otherwise rely on the collection permissions.
return super().check_permission(principals, bound_perms)


changeset = kinto.core.Service(name='collection-changeset',
path=CHANGESET_PATH,
Expand Down Expand Up @@ -220,8 +228,8 @@ class ChangeSetSchema(colander.MappingSchema):
def get_changeset(request):
bid = request.matchdict["bid"]
cid = request.matchdict["cid"]
bucket_uri = instance_uri(request, "bucket", id=bid)
collection_uri = instance_uri(request, "collection", bucket_id=bid, id=cid)

storage = request.registry.storage

queryparams = request.validated["querystring"]
filters = []
Expand All @@ -231,29 +239,40 @@ def get_changeset(request):
# Include tombstones when querying with _since
include_deleted = True

storage = request.registry.storage
if (bid, cid) == (MONITOR_BUCKET, CHANGES_COLLECTION):
model = ChangesModel(request)

# We'll make sure that data isn't changed while we read metadata, changes, etc.
before = storage.resource_timestamp(resource_name="record", parent_id=collection_uri)
# Fetch collection metadata.
metadata = storage.get(resource_name="collection", parent_id=bucket_uri, object_id=cid)
# Fetch list of changes.
changes = storage.list_all(
resource_name="record",
parent_id=collection_uri,
filters=filters,
id_field='id',
modified_field='last_modified',
deleted_field='deleted',
sorting=[Sort('last_modified', -1)],
include_deleted=include_deleted
)
# Fetch current collection timestamp.
timestamp = storage.resource_timestamp(resource_name="record", parent_id=collection_uri)
metadata = {}
timestamp = model.timestamp()
changes = model.get_objects(filters=filters, include_deleted=include_deleted)
# Redirect old since, on monitor/changes only.
_handle_old_since_redirect(request)

else:
bucket_uri = instance_uri(request, "bucket", id=bid)
collection_uri = instance_uri(request, "collection", bucket_id=bid, id=cid)

# Do not serve inconsistent data.
if before != timestamp: # pragma: no cover
raise storage_exceptions.IntegrityError(message="Inconsistent data. Retry.")
# We'll make sure that data isn't changed while we read metadata, changes, etc.
before = storage.resource_timestamp(resource_name="record", parent_id=collection_uri)
# Fetch collection metadata.
metadata = storage.get(resource_name="collection", parent_id=bucket_uri, object_id=cid)
# Fetch list of changes.
changes = storage.list_all(
resource_name="record",
parent_id=collection_uri,
filters=filters,
id_field='id',
modified_field='last_modified',
deleted_field='deleted',
sorting=[Sort('last_modified', -1)],
include_deleted=include_deleted
)
# Fetch current collection timestamp.
timestamp = storage.resource_timestamp(resource_name="record", parent_id=collection_uri)

# Do not serve inconsistent data.
if before != timestamp: # pragma: no cover
raise storage_exceptions.IntegrityError(message="Inconsistent data. Retry.")

# Cache control.
_handle_cache_expires(request, bid, cid)
Expand Down
35 changes: 35 additions & 0 deletions tests/test_changeset.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,38 @@ def test_extra_param_is_allowed(self):
def test_cache_control_headers_are_set(self):
resp = self.app.get(self.changeset_uri, headers=self.headers)
assert resp.headers['Cache-Control'] == 'max-age=1234'


class MonitorChangesetViewTest(BaseWebTest, unittest.TestCase):
records_uri = '/buckets/blocklists/collections/certificates/records'
changeset_uri = '/buckets/monitor/collections/changes/changeset?_expected=42'

def setUp(self):
super().setUp()
self.app.post_json(self.records_uri, SAMPLE_RECORD, headers=self.headers)

@classmethod
def get_app_settings(cls, extras=None):
settings = super().get_app_settings(extras)
settings["kinto.changes.since_max_age_days"] = 1
return settings

def test_changeset_exists_for_monitor_changes(self):
resp = self.app.head(self.records_uri, headers=self.headers)
records_timestamp = int(resp.headers["ETag"].strip('"'))

resp = self.app.get(self.changeset_uri)
data = resp.json

assert data["timestamp"] == records_timestamp
assert len(data["changes"]) == 1
assert data["changes"][0]["collection"] == "certificates"

def test_changeset_redirects_if_since_is_too_old(self):
resp = self.app.get(self.changeset_uri + '&_since="42"')

assert resp.status_code == 307
assert resp.headers["Location"] == (
'https://www.kinto-storage.org/v1'
'/buckets/monitor/collections/changes/records?_expected=42'
)

0 comments on commit 5444d59

Please sign in to comment.