Skip to content

Commit

Permalink
Add Hook Events for Chains and Safe Apps (#964)
Browse files Browse the repository at this point in the history
- Refactors the WebHook hook logic for the Safe Client Gateway client.
- A WebHook event is now represented as a `@dataclass` – `HookEvent`. This `HookEvent` can have two types:
  * `CHAIN_UPDATE` – represents chain related updates (more specifically updates to `Chain`, `GasPrice`, `Feature` – chain feature, and `Wallet`).
  * `SAFE_APPS_UPDATE` – represents safe app related updates (more specifically updates to `SafeApp`, `Provider`, `Tag` and `Feature` – safe app feature).
- The new web hooks are under a feature preview and can be enabled by setting the environment variable `FF_HOOK_EVENTS` to `true`.
  * If `FF_HOOK_EVENTS` is set to `false`, the `/v2/flush` endpoint is triggered (as before).
  • Loading branch information
fmrsabino authored Oct 4, 2023
1 parent 038500c commit 49cc0b5
Show file tree
Hide file tree
Showing 8 changed files with 1,113 additions and 57 deletions.
84 changes: 64 additions & 20 deletions src/chains/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,91 @@
from typing import Any

from django.conf import settings
from django.db.models.signals import post_delete, post_save
from django.db.models.signals import m2m_changed, post_delete, post_save, pre_delete
from django.dispatch import receiver

import clients.safe_client_gateway
from clients.safe_client_gateway import HookEvent, flush, hook_event

from .models import Chain, Feature, GasPrice, Wallet

logger = logging.getLogger(__name__)


def _flush_cgw_chains() -> None:
clients.safe_client_gateway.flush(
cgw_url=settings.CGW_URL,
cgw_flush_token=settings.CGW_FLUSH_TOKEN,
json={"invalidate": "Chains"},
)


@receiver(post_save, sender=Chain)
@receiver(post_delete, sender=Chain)
def on_chain_update(sender: Chain, **kwargs: Any) -> None:
def on_chain_update(sender: Chain, instance: Chain, **kwargs: Any) -> None:
logger.info("Chain update. Triggering CGW webhook")
_flush_cgw_chains()
if settings.FF_HOOK_EVENTS:
hook_event(HookEvent(type=HookEvent.Type.CHAIN_UPDATE, chain_id=instance.id))
else:
flush()


@receiver(post_save, sender=GasPrice)
@receiver(post_delete, sender=GasPrice)
def on_gas_price_update(sender: GasPrice, **kwargs: Any) -> None:
def on_gas_price_update(sender: GasPrice, instance: GasPrice, **kwargs: Any) -> None:
logger.info("GasPrice update. Triggering CGW webhook")
_flush_cgw_chains()
if settings.FF_HOOK_EVENTS:
hook_event(
HookEvent(type=HookEvent.Type.CHAIN_UPDATE, chain_id=instance.chain.id)
)
else:
flush()


# pre_delete is used because on pre_delete the model still has chains
# which is not the case on post_delete
@receiver(post_save, sender=Feature)
@receiver(post_delete, sender=Feature)
def on_feature_update(sender: Feature, **kwargs: Any) -> None:
@receiver(pre_delete, sender=Feature)
def on_feature_changed(sender: Feature, instance: Feature, **kwargs: Any) -> None:
logger.info("Feature update. Triggering CGW webhook")
_flush_cgw_chains()
if settings.FF_HOOK_EVENTS:
# A Feature change affects all the chains that have this feature
for chain in instance.chains.all():
hook_event(HookEvent(type=HookEvent.Type.CHAIN_UPDATE, chain_id=chain.id))
else:
flush()


@receiver(m2m_changed, sender=Feature.chains.through)
def on_feature_chains_changed(
sender: Feature, instance: Feature, action: str, pk_set: set[int], **kwargs: Any
) -> None:
logger.info("FeatureChains update. Triggering CGW webhook")
if action == "post_add" or action == "post_remove":
if settings.FF_HOOK_EVENTS:
for chain_id in pk_set:
hook_event(
HookEvent(type=HookEvent.Type.CHAIN_UPDATE, chain_id=chain_id)
)
else:
flush()


# pre_delete is used because on pre_delete the model still has chains
# which is not the case on post_delete
@receiver(post_save, sender=Wallet)
@receiver(post_delete, sender=Wallet)
def on_wallet_update(sender: Wallet, **kwargs: Any) -> None:
@receiver(pre_delete, sender=Wallet)
def on_wallet_changed(sender: Wallet, instance: Wallet, **kwargs: Any) -> None:
logger.info("Wallet update. Triggering CGW webhook")
_flush_cgw_chains()
if settings.FF_HOOK_EVENTS:
# A Wallet change affects all the chains that have this wallet
for chain in instance.chains.all():
hook_event(HookEvent(type=HookEvent.Type.CHAIN_UPDATE, chain_id=chain.id))
else:
flush()


@receiver(m2m_changed, sender=Wallet.chains.through)
def on_wallet_chains_changed(
sender: Wallet, instance: Wallet, action: str, pk_set: set[int], **kwargs: Any
) -> None:
logger.info("WalletChains update. Triggering CGW webhook")
if action == "post_add" or action == "post_remove":
if settings.FF_HOOK_EVENTS:
for chain_id in pk_set:
hook_event(
HookEvent(type=HookEvent.Type.CHAIN_UPDATE, chain_id=chain_id)
)
else:
flush()
8 changes: 6 additions & 2 deletions src/chains/tests/test_signals.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import responses
from django.test import TestCase, override_settings

from chains.models import Feature, Wallet
from chains.tests.factories import ChainFactory, GasPriceFactory
from ..models import Feature, Wallet
from ..tests.factories import ChainFactory, GasPriceFactory


@override_settings(
FF_HOOK_EVENTS=False,
CGW_URL="http://127.0.0.1",
CGW_FLUSH_TOKEN="example-token",
)
Expand Down Expand Up @@ -113,6 +114,7 @@ def test_on_chain_update_with_no_flush_token_set(self) -> None:


@override_settings(
FF_HOOK_EVENTS=False,
CGW_URL="http://127.0.0.1",
CGW_FLUSH_TOKEN="example-token",
)
Expand Down Expand Up @@ -165,6 +167,7 @@ def test_on_feature_update_hook_call(self) -> None:


@override_settings(
FF_HOOK_EVENTS=False,
CGW_URL="http://127.0.0.1",
CGW_FLUSH_TOKEN="example-token",
)
Expand Down Expand Up @@ -217,6 +220,7 @@ def test_on_wallet_update_hook_call(self) -> None:


@override_settings(
FF_HOOK_EVENTS=False,
CGW_URL="http://127.0.0.1",
CGW_FLUSH_TOKEN="example-token",
)
Expand Down
Loading

0 comments on commit 49cc0b5

Please sign in to comment.