# Path: kinto-remote-settings/src/kinto_remote_settings/signer/serializer.py
# def canonical_json(records, last_modified):
#     records = (r for r in records if not r.get("deleted", False))
#     records = sorted(records, key=operator.itemgetter("id"))
# 
#     payload = {"data": records, "last_modified": "%s" % last_modified}
# 
#     dump = canonicaljson.dumps(payload)
# 
#     return dump
# 
# Path: kinto-remote-settings/src/kinto_remote_settings/signer/utils.py
# class STATUS(Enum):
#     WORK_IN_PROGRESS = "work-in-progress"
#     TO_SIGN = "to-sign"
#     TO_REFRESH = "to-resign"
#     TO_REVIEW = "to-review"
#     TO_ROLLBACK = "to-rollback"
#     SIGNED = "signed"
# 
#     def __eq__(self, other):
#         if not hasattr(other, "value"):
#             return self.value == other
#         return super(STATUS, self).__eq__(other)
# 
#     def __ne__(self, other):
#         return not self.__eq__(other)
# 
# def ensure_resource_exists(
#     request, resource_name, parent_id, obj, permissions, matchdict
# ):
#     storage = request.registry.storage
#     permission = request.registry.permission
#     try:
#         created = storage.create(
#             resource_name=resource_name, parent_id=parent_id, obj=obj
#         )
#         object_uri = instance_uri(request, resource_name, **matchdict)
#         permission.replace_object_permissions(object_uri, permissions)
#         notify_resource_event(
#             request,
#             {"method": "PUT", "path": object_uri},
#             matchdict=matchdict,
#             resource_name=resource_name,
#             parent_id=parent_id,
#             obj=created,
#             action=ACTIONS.CREATE,
#         )
#     except UnicityError:
#         pass
# 
# def notify_resource_event(
#     request, request_options, matchdict, resource_name, parent_id, obj, action, old=None
# ):
#     """Helper that triggers resource events as real requests."""
#     fakerequest = build_request(request, request_options)
#     fakerequest.matchdict = matchdict
#     fakerequest.bound_data = request.bound_data
#     fakerequest.authn_type, fakerequest.selected_userid = PLUGIN_USERID.split(":")
#     fakerequest.current_resource_name = resource_name
# 
#     # When kinto-signer copies record from one place to another,
#     # it simulates a resource event. Since kinto-attachment
#     # prevents from updating attachment fields, it throws an error.
#     # The following flag will disable the kinto-attachment check.
#     # See https://github.com/Kinto/kinto-signer/issues/256
#     # and https://bugzilla.mozilla.org/show_bug.cgi?id=1470812
#     has_changed_attachment = (
#         resource_name == "record"
#         and action == ACTIONS.UPDATE
#         and "attachment" in old
#         and old["attachment"] != obj.get("attachment")
#     )
#     if has_changed_attachment:
#         fakerequest._attachment_auto_save = True
# 
#     fakerequest.notify_resource_event(
#         parent_id=parent_id,
#         timestamp=obj[FIELD_LAST_MODIFIED],
#         data=obj,
#         action=action,
#         old=old,
#     )
# 
# def records_diff(left, right):
#     left_by_id = {r["id"]: r for r in left}
#     results = []
#     for r in right:
#         rid = r["id"]
#         left_record = left_by_id.pop(rid, None)
#         if left_record is None:
#             # In right, but not in left (deleted!)
#             results.append({**r, "deleted": True})
#         elif not records_equal(left_record, r):
#             # Differ between left and right
#             results.append(left_record)
#     # In left, but not in right.
#     results.extend(left_by_id.values())
#     return results


import datetime
import logging
from enum import Enum
from kinto.core.events import ACTIONS
from kinto.core.storage.exceptions import RecordNotFoundError
from pyramid.authorization import Everyone
from .serializer import canonical_json
from .utils import STATUS, ensure_resource_exists, notify_resource_event, records_diff
        0. Create the destination bucket / collection
        1. Get all the records of the collection
        2. Send all records since the last_modified of the destination
        3. Compute a hash of these records
        4. Ask the signer for a signature
        5. Send the signature to the destination.
        """
        changes_count = 0

        self.create_destination(request)

        if push_records:
            changes_count = self.push_records_to_destination(request)

        records, timestamp = self.get_destination_records(empty_none=False)
        serialized_records = canonical_json(records, timestamp)
        logger.debug(f"{self.source_collection_uri}:\t'{serialized_records}'")
        signature = self.signer.sign(serialized_records)

        self.set_destination_signature(signature, source_attributes, request)
        if next_source_status is not None:
            self.update_source_status(
                next_source_status, request, previous_source_status
            )

        return changes_count

    def refresh_signature(self, request, next_source_status=None):
        """Refresh the signature without moving records."""
        records, timestamp = self.get_destination_records(empty_none=False)

# Path: kinto-remote-settings/src/kinto_remote_settings/signer/serializer.py
# def canonical_json(records, last_modified):
#     records = (r for r in records if not r.get("deleted", False))
#     records = sorted(records, key=operator.itemgetter("id"))
# 
#     payload = {"data": records, "last_modified": "%s" % last_modified}
# 
#     dump = canonicaljson.dumps(payload)
# 
#     return dump
# 
# Path: kinto-remote-settings/src/kinto_remote_settings/signer/utils.py
# class STATUS(Enum):
#     WORK_IN_PROGRESS = "work-in-progress"
#     TO_SIGN = "to-sign"
#     TO_REFRESH = "to-resign"
#     TO_REVIEW = "to-review"
#     TO_ROLLBACK = "to-rollback"
#     SIGNED = "signed"
# 
#     def __eq__(self, other):
#         if not hasattr(other, "value"):
#             return self.value == other
#         return super(STATUS, self).__eq__(other)
# 
#     def __ne__(self, other):
#         return not self.__eq__(other)
# 
# def ensure_resource_exists(
#     request, resource_name, parent_id, obj, permissions, matchdict
# ):
#     storage = request.registry.storage
#     permission = request.registry.permission
#     try:
#         created = storage.create(
#             resource_name=resource_name, parent_id=parent_id, obj=obj
#         )
#         object_uri = instance_uri(request, resource_name, **matchdict)
#         permission.replace_object_permissions(object_uri, permissions)
#         notify_resource_event(
#             request,
#             {"method": "PUT", "path": object_uri},
#             matchdict=matchdict,
#             resource_name=resource_name,
#             parent_id=parent_id,
#             obj=created,
#             action=ACTIONS.CREATE,
#         )
#     except UnicityError:
#         pass
# 
# def notify_resource_event(
#     request, request_options, matchdict, resource_name, parent_id, obj, action, old=None
# ):
#     """Helper that triggers resource events as real requests."""
#     fakerequest = build_request(request, request_options)
#     fakerequest.matchdict = matchdict
#     fakerequest.bound_data = request.bound_data
#     fakerequest.authn_type, fakerequest.selected_userid = PLUGIN_USERID.split(":")
#     fakerequest.current_resource_name = resource_name
# 
#     # When kinto-signer copies record from one place to another,
#     # it simulates a resource event. Since kinto-attachment
#     # prevents from updating attachment fields, it throws an error.
#     # The following flag will disable the kinto-attachment check.
#     # See https://github.com/Kinto/kinto-signer/issues/256
#     # and https://bugzilla.mozilla.org/show_bug.cgi?id=1470812
#     has_changed_attachment = (
#         resource_name == "record"
#         and action == ACTIONS.UPDATE
#         and "attachment" in old
#         and old["attachment"] != obj.get("attachment")
#     )
#     if has_changed_attachment:
#         fakerequest._attachment_auto_save = True
# 
#     fakerequest.notify_resource_event(
#         parent_id=parent_id,
#         timestamp=obj[FIELD_LAST_MODIFIED],
#         data=obj,
#         action=action,
#         old=old,
#     )
# 
# def records_diff(left, right):
#     left_by_id = {r["id"]: r for r in left}
#     results = []
#     for r in right:
#         rid = r["id"]
#         left_record = left_by_id.pop(rid, None)
#         if left_record is None:
#             # In right, but not in left (deleted!)
#             results.append({**r, "deleted": True})
#         elif not records_equal(left_record, r):
#             # Differ between left and right
#             results.append(left_record)
#     # In left, but not in right.
#     results.extend(left_by_id.values())
#     return results


# Path: kinto-remote-settings/src/kinto_remote_settings/signer/updater.py
import datetime
import logging
from enum import Enum
from kinto.core.events import ACTIONS
from kinto.core.storage.exceptions import RecordNotFoundError
from pyramid.authorization import Everyone
from .serializer import canonical_json
from .utils import STATUS, ensure_resource_exists, notify_resource_event, records_diff

        0. Create the destination bucket / collection
        1. Get all the records of the collection
        2. Send all records since the last_modified of the destination
        3. Compute a hash of these records
        4. Ask the signer for a signature
        5. Send the signature to the destination.
        """
        changes_count = 0

        self.create_destination(request)

        if push_records:
            changes_count = self.push_records_to_destination(request)

        records, timestamp = self.get_destination_records(empty_none=False)
        serialized_records = canonical_json(records, timestamp)
        logger.debug(f"{self.source_collection_uri}:\t'{serialized_records}'")
        signature = self.signer.sign(serialized_records)

        self.set_destination_signature(signature, source_attributes, request)
        if next_source_status is not None:
            self.update_source_status(
                next_source_status, request, previous_source_status
            )

        return changes_count

    def refresh_signature(self, request, next_source_status=None):
        """Refresh the signature without moving records."""
        records, timestamp = self.get_destination_records(empty_none=False)

        serialized_records = canonical_json(records, timestamp)