.. _notifications: Notifications ############# Knowing some records have been modified in a resource is very useful to integrate a Kinto-Core-based application with other services. For example, a search service that gets notified everytime something has changed, can continuously update its index. Kinto-Core leverages Pyramid's built-in event system and produces the following events: - :class:`kinto.core.events.ResourceRead`: a read operation occured on the resource. - :class:`kinto.core.events.ResourceChanged`: a resource **is being changed**. This event occurs synchronously within the transaction and within the request/response cycle. Commit is not yet done and rollback is still possible. Subscribers of this event are likely to perform database operations, alter the server response, or cancel the transaction (by raising an HTTP exception for example). Do not subscribe to this event for operations that will not be rolled-back automatically. - :class:`kinto.core.events.AfterResourceChanged`: a resource **was changed** and **committed**. Subscribers of this event can fail, errors are swallowed and logged. The final transaction result (or response) cannot be altered. Subscribers of this event are likely to perform irreversible actions that requires data to be committed in database (like sending messages, deleting files on disk, or run asynchronous tasks). Event subscribers can then pick up those events and act upon them. .. code-block:: python from kinto.core.events import AfterResourceChanged def on_resource_changed(event): for change in event.impacted_records: start_download(change['new']['url']) config.add_subscriber(on_resource_changed, AfterResourceChanged) .. autoclass:: kinto.core.events.ResourceRead .. autoclass:: kinto.core.events.ResourceChanged .. autoclass:: kinto.core.events.AfterResourceChanged It can be useful to emit events, especially if you wish to simulate resources (as in e.g. `kinto-changes`). .. autofunction:: kinto.core.events.notify_resource_event Transactions ------------ Only one event is sent per transaction, per resource and per action. In other words, if every requests of a :ref:`batch requests ` perform the same action on the same resource, only one event will be sent. The ``AfterResourceChanged`` is sent only if the transaction was comitted successfully. It is possible to cancel the current transaction by raising an HTTP Exception from a ``ResourceChanged`` event. For example: .. code-block:: python from kinto.core.events import ResourceChanged from pyramid import httpexceptions def check_quota(event): max_quota = event.request.registry.settings['max_quota'] if check_quota(event, max_quota): raise httpexceptions.HTTPInsufficientStorage() config.add_subscriber(check_quota, ResourceChanged) Filtering --------- It is possible to filter events based on its action or the name of the resource where it occured. For example: .. code-block:: python from kinto.core.events import ResourceChanged, ACTIONS config.add_subscriber(on_mushroom_changed, ResourceChanged, for_resources=('mushroom',)) config.add_subscriber(on_record_deleted, ResourceChanged, for_actions=(ACTIONS.DELETE,)) Payload ------- The :class:`kinto.core.events.ResourceChanged` and :class:`kinto.core.events.AfterResourceChanged` events contain a ``payload`` attribute with the following information: - **timestamp**: the collection timestamp of the ``(parent_id, resource_name)`` pair for which this event was sent - **action**: what happened. 'create', 'update' or 'delete' - **uri**: the uri of the impacted resource - **user_id**: the authenticated user id - **resource_name**: the name of the impacted resouce (e.g. 'article', 'bookmark', bucket', 'group' etc.) - **_id**: id of the impacted record - ****: every value matched by each URL pattern name (see `Pyramid request matchdict `_) And provides the list of affected records in the ``impacted_records`` attribute. This list contains dictionaries with ``new`` and ``old`` keys. For creation events, only ``new`` is provided. For deletion events, only ``old`` is provided. This also allows listeners to react on particular field change or handle *diff* between versions. Example, when deleting a collection with two records: :: >>> event.impacted_records [{'old': {'deleted': True, 'last_modified': 1447240896769, 'id': 'a1f4af60-ddf5-4c49-933f-4cfeff18ad07'}}, {'old': {'deleted': True, 'last_modified': 1447240896770, 'id': '7a6916aa-0ea1-42a7-9741-c24fe13cb70b'}}] Event listeners --------------- It is possible for an application or a plugin to listen to events and execute some code. Triggered code on events is synchronously called when a request is handled. An event handler can itself trigger other events. *Kinto-Core* offers custom listeners that can be activated through configuration, so that every Kinto-Core-based application can benefit from **pluggable listeners** without using `config.add_subscriber()` explicitely. Currently, a simple built-in listener is available in kinto-redis plugin, that just delivers the events into a Redis queue, allowing asynchronous event handling. See `Kinto Redis driver plugin repository `_ for more information. To activate it, look at :ref:`the dedicated configuration `. Implementing a custom listener consists on implementing the following interface: .. autoclass:: kinto.core.listeners.ListenerBase :members: __call__