1
1
"""Admin server classes."""
2
2
3
3
import asyncio
4
+ from hmac import compare_digest
4
5
import logging
5
6
import re
7
+ from typing import Callable , Coroutine
6
8
import uuid
9
+ import warnings
7
10
8
- from typing import Callable , Coroutine , Sequence , Set
9
-
10
- import aiohttp_cors
11
- import jwt
12
-
13
- from hmac import compare_digest
14
11
from aiohttp import web
15
12
from aiohttp_apispec import (
16
13
docs ,
17
14
response_schema ,
18
15
setup_aiohttp_apispec ,
19
16
validation_middleware ,
20
17
)
21
-
18
+ import aiohttp_cors
19
+ import jwt
22
20
from marshmallow import fields
23
21
24
22
from ..config .injection_context import InjectionContext
25
- from ..core .profile import Profile
23
+ from ..core .event_bus import Event , EventBus
26
24
from ..core .plugin_registry import PluginRegistry
25
+ from ..core .profile import Profile
27
26
from ..ledger .error import LedgerConfigError , LedgerTransactionError
28
27
from ..messaging .models .openapi import OpenAPISchema
29
28
from ..messaging .responder import BaseResponder
30
- from ..transport .queue .basic import BasicMessageQueue
29
+ from ..multitenant .manager import MultitenantManager , MultitenantManagerError
30
+ from ..storage .error import StorageNotFoundError
31
31
from ..transport .outbound .message import OutboundMessage
32
+ from ..transport .queue .basic import BasicMessageQueue
32
33
from ..utils .stats import Collector
33
34
from ..utils .task_queue import TaskQueue
34
35
from ..version import __version__
35
- from ..multitenant .manager import MultitenantManager , MultitenantManagerError
36
-
37
- from ..storage .error import StorageNotFoundError
38
36
from .base_server import BaseAdminServer
39
37
from .error import AdminSetupError
40
38
from .request_context import AdminRequestContext
41
39
42
-
43
40
LOGGER = logging .getLogger (__name__ )
44
41
42
+ EVENT_PATTERN_WEBHOOK = re .compile ("^acapy::webhook::(.*)$" )
43
+ EVENT_PATTERN_RECORD = re .compile ("^acapy::record::(.*)::(.*)$" )
44
+
45
+ EVENT_WEBHOOK_MAPPING = {
46
+ "acapy::basicmessage::received" : "basicmessages" ,
47
+ "acapy::problem_report" : "problem_report" ,
48
+ "acapy::ping::received" : "ping" ,
49
+ "acapy::ping::response_received" : "ping" ,
50
+ "acapy::actionmenu::received" : "actionmenu" ,
51
+ "acapy::actionmenu::get-active-menu" : "get-active-menu" ,
52
+ "acapy::actionmenu::perform-menu-action" : "perform-menu-action" ,
53
+ }
54
+
45
55
46
56
class AdminModulesSchema (OpenAPISchema ):
47
57
"""Schema for the modules endpoint."""
@@ -93,7 +103,6 @@ def __init__(
93
103
self ,
94
104
profile : Profile ,
95
105
send : Coroutine ,
96
- webhook : Coroutine ,
97
106
** kwargs ,
98
107
):
99
108
"""
@@ -106,7 +115,6 @@ def __init__(
106
115
super ().__init__ (** kwargs )
107
116
self ._profile = profile
108
117
self ._send = send
109
- self ._webhook = webhook
110
118
111
119
async def send_outbound (self , message : OutboundMessage ):
112
120
"""
@@ -119,53 +127,23 @@ async def send_outbound(self, message: OutboundMessage):
119
127
120
128
async def send_webhook (self , topic : str , payload : dict ):
121
129
"""
122
- Dispatch a webhook.
130
+ Dispatch a webhook. DEPRECATED: use the event bus instead.
123
131
124
132
Args:
125
133
topic: the webhook topic identifier
126
134
payload: the webhook payload value
127
135
"""
128
- await self ._webhook (self ._profile , topic , payload )
136
+ warnings .warn (
137
+ "responder.send_webhook is deprecated; please use the event bus instead." ,
138
+ DeprecationWarning ,
139
+ )
140
+ await self ._profile .notify ("acapy::webhook::" + topic , payload )
129
141
130
142
@property
131
143
def send_fn (self ) -> Coroutine :
132
144
"""Accessor for async function to send outbound message."""
133
145
return self ._send
134
146
135
- @property
136
- def webhook_fn (self ) -> Coroutine :
137
- """Accessor for the async function to dispatch a webhook."""
138
- return self ._webhook
139
-
140
-
141
- class WebhookTarget :
142
- """Class for managing webhook target information."""
143
-
144
- def __init__ (
145
- self ,
146
- endpoint : str ,
147
- topic_filter : Sequence [str ] = None ,
148
- max_attempts : int = None ,
149
- ):
150
- """Initialize the webhook target."""
151
- self .endpoint = endpoint
152
- self .max_attempts = max_attempts
153
- self ._topic_filter = None
154
- self .topic_filter = topic_filter # call setter
155
-
156
- @property
157
- def topic_filter (self ) -> Set [str ]:
158
- """Accessor for the target's topic filter."""
159
- return self ._topic_filter
160
-
161
- @topic_filter .setter
162
- def topic_filter (self , val : Sequence [str ]):
163
- """Setter for the target's topic filter."""
164
- filt = set (val ) if val else None
165
- if filt and "*" in filt :
166
- filt = None
167
- self ._topic_filter = filt
168
-
169
147
170
148
@web .middleware
171
149
async def ready_middleware (request : web .BaseRequest , handler : Coroutine ):
@@ -270,7 +248,6 @@ def __init__(
270
248
self .root_profile = root_profile
271
249
self .task_queue = task_queue
272
250
self .webhook_router = webhook_router
273
- self .webhook_targets = {}
274
251
self .websocket_queues = {}
275
252
self .site = None
276
253
self .multitenant_manager = context .inject (MultitenantManager , required = False )
@@ -371,7 +348,6 @@ async def setup_context(request: web.Request, handler):
371
348
responder = AdminResponder (
372
349
profile ,
373
350
self .outbound_message_router ,
374
- self .send_webhook ,
375
351
)
376
352
profile .context .injector .bind_instance (BaseResponder , responder )
377
353
@@ -472,6 +448,19 @@ def sort_dict(raw: dict) -> dict:
472
448
if plugin_registry :
473
449
plugin_registry .post_process_routes (self .app )
474
450
451
+ event_bus = self .context .inject (EventBus , required = False )
452
+ if event_bus :
453
+ event_bus .subscribe (EVENT_PATTERN_WEBHOOK , self .__on_webhook_event )
454
+ event_bus .subscribe (EVENT_PATTERN_RECORD , self .__on_record_event )
455
+
456
+ for event_topic , webhook_topic in EVENT_WEBHOOK_MAPPING .items ():
457
+ event_bus .subscribe (
458
+ re .compile (re .escape (event_topic )),
459
+ lambda profile , event , webhook_topic = webhook_topic : self .send_webhook (
460
+ profile , webhook_topic , event .payload
461
+ ),
462
+ )
463
+
475
464
# order tags alphabetically, parameters deterministically and pythonically
476
465
swagger_dict = self .app ._state ["swagger_dict" ]
477
466
swagger_dict .get ("tags" , []).sort (key = lambda t : t ["name" ])
@@ -799,21 +788,17 @@ async def websocket_handler(self, request):
799
788
800
789
return ws
801
790
802
- def add_webhook_target (
803
- self ,
804
- target_url : str ,
805
- topic_filter : Sequence [str ] = None ,
806
- max_attempts : int = None ,
807
- ):
808
- """Add a webhook target."""
809
- self .webhook_targets [target_url ] = WebhookTarget (
810
- target_url , topic_filter , max_attempts
811
- )
791
+ async def __on_webhook_event (self , profile : Profile , event : Event ):
792
+ match = EVENT_PATTERN_WEBHOOK .search (event .topic )
793
+ webhook_topic = match .group (1 ) if match else None
794
+ if webhook_topic :
795
+ await self .send_webhook (profile , webhook_topic , event .payload )
812
796
813
- def remove_webhook_target (self , target_url : str ):
814
- """Remove a webhook target."""
815
- if target_url in self .webhook_targets :
816
- del self .webhook_targets [target_url ]
797
+ async def __on_record_event (self , profile : Profile , event : Event ):
798
+ match = EVENT_PATTERN_RECORD .search (event .topic )
799
+ webhook_topic = match .group (1 ) if match else None
800
+ if webhook_topic :
801
+ await self .send_webhook (profile , webhook_topic , event .payload )
817
802
818
803
async def send_webhook (self , profile : Profile , topic : str , payload : dict ):
819
804
"""Add a webhook to the queue, to send to all registered targets."""
@@ -825,8 +810,6 @@ async def send_webhook(self, profile: Profile, topic: str, payload: dict):
825
810
metadata = {"x-wallet-id" : wallet_id }
826
811
827
812
if self .webhook_router :
828
- # for idx, target in self.webhook_targets.items():
829
- # if not target.topic_filter or topic in target.topic_filter:
830
813
for endpoint in webhook_urls :
831
814
self .webhook_router (
832
815
topic ,
0 commit comments