def get_changeset()

in kinto-remote-settings/src/kinto_remote_settings/changes/views.py [0:0]


def get_changeset(request):
    bid = request.matchdict["bucket_id"]
    cid = request.matchdict["collection_id"]

    storage = request.registry.storage

    queryparams = request.validated["querystring"]
    limit = bound_limit(request.registry.settings, queryparams.get("_limit"))
    filters = []
    include_deleted = False
    if "_since" in queryparams:
        filters = [Filter("last_modified", queryparams["_since"], COMPARISON.GT)]
        # Include tombstones when querying with _since
        include_deleted = True

    if (bid, cid) == (MONITOR_BUCKET, CHANGES_COLLECTION):
        # Redirect old since, on monitor/changes only.
        _handle_old_since_redirect(request)

        if "bucket" in queryparams:
            filters.append(Filter("bucket", queryparams["bucket"], COMPARISON.EQ))

        if "collection" in queryparams:
            filters.append(
                Filter("collection", queryparams["collection"], COMPARISON.EQ)
            )

        model = ChangesModel(request)
        metadata = {}
        records_timestamp = model.timestamp()
        last_modified = (
            records_timestamp  # The collection 'monitor/changes' is virtual.
        )
        # Mimic records endpoint and sort by timestamp desc.
        sorting = [Sort("last_modified", -1)]
        changes = model.get_objects(
            filters=filters,
            limit=limit,
            include_deleted=include_deleted,
            sorting=sorting,
        )

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

        try:
            # 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
            )

        except storage_exceptions.ObjectNotFoundError:
            raise httpexceptions.HTTPNotFound()

        except storage_exceptions.BackendError as e:
            # The call to `resource_timestamp()` on an empty collection will try
            # initialize it. If the instance is read-only, it fails with a backend
            # error. Raise 404 in this case otherwise raise the original backend error.
            if "when running in readonly" in str(e):
                raise httpexceptions.HTTPNotFound()
            raise

        # Fetch list of changes.
        changes = storage.list_all(
            resource_name="record",
            parent_id=collection_uri,
            filters=filters,
            limit=limit,
            id_field="id",
            modified_field="last_modified",
            deleted_field="deleted",
            sorting=[Sort("last_modified", -1)],
            include_deleted=include_deleted,
        )
        # Fetch current collection timestamp.
        records_timestamp = storage.resource_timestamp(
            resource_name="record", parent_id=collection_uri
        )
        # We use the timestamp from the collection metadata, because we want it to
        # be bumped when the signature is refreshed. Indeed, the CDN will revalidate
        # the origin's response, only if the `Last-Modified` header has changed.
        # Side note: We are sure that the collection timestamp is always higher
        # than the records timestamp, because we have fields like `last_edit_date`
        # in the collection metadata that are automatically bumped when records change.
        last_modified = metadata["last_modified"]

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

    # Cache control.
    _handle_cache_expires(request, bid, cid)

    # Set Last-Modified response header (Pyramid takes care of converting).
    request.response.last_modified = last_modified / 1000.0

    data = {
        "metadata": {
            **metadata,
            "bucket": bid,
        },
        "timestamp": records_timestamp,
        "changes": changes,
    }
    return data