1
+ from aiohttp import (
2
+ web ,
3
+ ClientSession ,
4
+ ClientRequest ,
5
+ )
6
+ from dataclasses import dataclass
7
+ from pubsub import pub
8
+ import sys
9
+
10
+ from .controllers .connections import ConnectionsController
11
+ from .controllers .messaging import MessagingController
12
+ from .controllers .mediation import MediationController
13
+ from .controllers .schema import SchemaController
14
+ from .controllers .wallet import WalletController
15
+ from .controllers .definitions import DefinitionsController
16
+ from .controllers .issuer import IssuerController
17
+ from .controllers .proof import ProofController
18
+ from .controllers .ledger import LedgerController
19
+ from .controllers .credential import CredentialController
20
+ from .controllers .multitenant import MultitenancyController
21
+ from .controllers .server import ServerController
22
+ from .controllers .oob import OOBController
23
+ from .controllers .action_menu import ActionMenuController
24
+ from .controllers .revocation import RevocationController
25
+
26
+ # from .aries_base_controller import AriesBaseController
27
+ from .aries_webhook_listener import AriesWebhookListener
28
+
29
+ import logging
30
+
31
+ logger = logging .getLogger ("aries_controller" )
32
+
33
+
34
+ @dataclass
35
+ class AriesAgentController :
36
+ """The Aries Agent Controller class
37
+
38
+ This class allows you to interact with Aries by exposing the aca-py API.
39
+
40
+ Attributes
41
+ ----------
42
+ webhook_host : str
43
+ The url of the webhook host
44
+ webhook_port : int
45
+ The exposed port for webhooks on the host
46
+ admin_url : str
47
+ The URL for the Admin API
48
+ webhook_base : str
49
+ The base url for webhooks (default is "")
50
+ connections : bool
51
+ Specify whether to create connecitons (default is True)
52
+ messaging : bool
53
+ Initialise the messaging interface (default is True)
54
+ is_multitenant : bool
55
+ Initialise the multitenant interface (default is False)
56
+ mediation : bool
57
+ Initialise the mediation interface (default is False)
58
+ issuer : bool
59
+ Initialise the issuer interface (defautl is True)
60
+ action_menu : bool
61
+ Initialise the action menu interface (default is True)
62
+ revocations : bool
63
+ Initialise revocation interface for credentials (default is True)
64
+ api_key : str
65
+ The API key (default is None)
66
+ tenant_jwt: str
67
+ The tenant JW token (default is None)
68
+ wallet_id : str
69
+ The tenant wallet identifier
70
+ """
71
+
72
+ # TODO rethink how to initialise. Too many args?
73
+ # is it important to let users config connections/issuer etc
74
+ admin_url : str
75
+ webhook_host : str = None
76
+ webhook_port : int = None
77
+ webhook_base : str = ""
78
+ # connections: bool = True
79
+ # messaging: bool = True
80
+ is_multitenant : bool = False
81
+ mediation : bool = False
82
+ # issuer: bool = True
83
+ # action_menu: bool = True
84
+ # revocations: bool = True
85
+ api_key : str = None
86
+ tenant_jwt : str = None
87
+ wallet_id : str = None
88
+
89
+ def __post_init__ (self ):
90
+ """Constructs additional attributes,
91
+ and logic defined by attributes set during initial instantiation
92
+ """
93
+
94
+ self .webhook_site = None
95
+ self .connections_controller = None
96
+
97
+ # Construct headers for Client Session and the session itself
98
+ self .headers = {}
99
+
100
+ if self .api_key :
101
+ self .headers .update ({"X-API-Key" : self .api_key })
102
+
103
+ if self .tenant_jwt :
104
+ self .headers .update ({'Authorization' : 'Bearer ' + self .tenant_jwt , 'content-type' : "application/json" })
105
+
106
+ self .client_session : ClientSession = ClientSession (headers = self .headers )
107
+
108
+ self .webhook_listener : AriesWebhookListener = AriesWebhookListener (webhook_host = self .webhook_host , webhook_port = self .webhook_port , webhook_base = self .webhook_base , is_multitenant = self .is_multitenant )
109
+
110
+ # Instantiate controllers based on the provided attributes
111
+ # if self.connections:
112
+ self .connections = ConnectionsController (self .admin_url , self .client_session )
113
+
114
+ # if self.messaging:
115
+ self .messaging = MessagingController (self .admin_url , self .client_session )
116
+
117
+ self .proofs = ProofController (self .admin_url , self .client_session )
118
+ self .ledger = LedgerController (self .admin_url , self .client_session )
119
+ self .credentials = CredentialController (self .admin_url , self .client_session )
120
+ self .server = ServerController (self .admin_url , self .client_session )
121
+ self .oob = OOBController (self .admin_url , self .client_session )
122
+
123
+ if self .is_multitenant :
124
+ self .multitenant = MultitenancyController (self .admin_url , self .client_session )
125
+
126
+ if self .mediation :
127
+ self .mediation = MediationController (self .admin_url , self .client_session )
128
+
129
+ # if self.issuer:
130
+ self .schema = SchemaController (self .admin_url , self .client_session )
131
+ self .wallet = WalletController (self .admin_url , self .client_session )
132
+ self .definitions = DefinitionsController (self .admin_url , self .client_session )
133
+ self .issuer = IssuerController (self .admin_url , self .client_session , self .connections ,
134
+ self .wallet , self .definitions )
135
+
136
+ # if self.action_menu:
137
+ self .action_menu = ActionMenuController (self .admin_url , self .client_session )
138
+
139
+ # if self.revocations:
140
+ self .revocations = RevocationController (
141
+ self .admin_url ,
142
+ self .client_session
143
+ )
144
+
145
+ def update_wallet_id (self , wallet_id : str ):
146
+ """This wallet_id is used to register for webhooks specific to this sub_wallet
147
+
148
+ Args:
149
+ ----
150
+ wallet_id : str
151
+ The tenant wallet identifier
152
+ """
153
+ self .wallet_id = wallet_id
154
+
155
+
156
+ def update_tenant_jwt (self , tenant_jwt : str , wallet_id : str ):
157
+ """Update the tenant JW token attribute and the header
158
+
159
+ Args:
160
+ ----
161
+ tenant_jwt : str
162
+ The tenant's JW token
163
+ wallet_id : str
164
+ The tenant wallet identifier
165
+ """
166
+ self .tenant_jwt = tenant_jwt
167
+ self .update_wallet_id (wallet_id )
168
+ self .headers .update ({'Authorization' : 'Bearer ' + tenant_jwt , 'content-type' : "application/json" })
169
+ self .client_session .headers .update (self .headers )
170
+
171
+
172
+ def update_api_key (self , api_key : str ):
173
+ """Update the API Key attribute and the header
174
+
175
+ Args:
176
+ ----
177
+ api_key : str
178
+ The API Key
179
+ """
180
+ self .api_key = api_key
181
+ self .headers .update ({"X-API-Key" : api_key })
182
+ self .client_session .headers .update (self .headers )
183
+
184
+
185
+ def remove_api_key (self ):
186
+ """Removes the API key attribute and corresponding headers from the Client Session"""
187
+ self .api_key = None
188
+ if 'X-API-Key' in self .client_session .headers :
189
+ del self .client_session .headers ['X-API-Key' ]
190
+ del self .headers ['X-API-Key' ]
191
+
192
+
193
+ def remove_tenant_jwt (self ):
194
+ """Removes the tenant's JW Token attribute and corresponding headers from the Client Session"""
195
+ self .tenant_jwt = None
196
+ if 'Authorization' in self .client_session .headers :
197
+ del self .client_session .headers ['Authorization' ]
198
+ del self .headers ['Authorization' ]
199
+ if 'content-type' in self .client_session .headers :
200
+ del self .client_session .headers ['content-type' ]
201
+ del self .headers ['content-type' ]
202
+
203
+
204
+ def register_listeners (self , listeners , defaults = True ):
205
+ """Registers the webhook listners
206
+
207
+ Args:
208
+ ----
209
+ listeners : [dict]
210
+ A collection of dictionaries comprised of a "handler": handler (fct) and a "topic":"topicname" key-value pairs
211
+ defaults : bool
212
+ Whether to connect to the default handlers for connections, basicmessage and present_proof
213
+ (default is True)
214
+ """
215
+ try :
216
+ if defaults :
217
+ if self .connections :
218
+ pub .subscribe (self .connections .default_handler , "connections" )
219
+ if self .messaging :
220
+ pub .subscribe (self .messaging .default_handler , "basicmessages" )
221
+ if self .proofs :
222
+ pub .subscribe (self .proofs .default_handler , "present_proof" )
223
+
224
+ for listener in listeners :
225
+ self .add_listener (listener )
226
+ except Exception as exc :
227
+ print (f"Register webhooks listeners failed! { exc !r} occurred." )
228
+ logger .warn (f"Register webhooks listeners failed! { exc !r} occurred." )
229
+
230
+ def add_listener (self , listener ):
231
+ """Subscribe to a listeners for a topic
232
+
233
+ Args:
234
+ ----
235
+ listener : dict
236
+ A dictionary comprised of a "handler": handler (fct) and a "topic":"topicname" key-value pairs
237
+ """
238
+ try :
239
+ pub_topic_path = listener ['topic' ]
240
+ if self .wallet_id :
241
+ pub_topic_path = f"{ self .wallet_id } .{ pub_topic_path } "
242
+ print ("Subscribing too: " + pub_topic_path )
243
+ pub .subscribe (listener ["handler" ], pub_topic_path )
244
+
245
+ logger .debug ("Lister added for topic : " , pub_topic_path )
246
+ except Exception as exc :
247
+ print (f"Adding webhooks listener failed! { exc !r} occurred." )
248
+ logger .warn (f"Adding webhooks listener failed! { exc !r} occurred." )
249
+
250
+
251
+
252
+ def remove_listener (self , listener ):
253
+ """Remove a listener for a topic
254
+
255
+ Args:
256
+ ----
257
+ listener : dict
258
+ A dictionary comprised of a "handler": handler (fct) and a "topic":"topicname" key-value pairs
259
+ """
260
+ try :
261
+ if pub .isSubscribed (listener ["handler" ], listener ["topic" ]):
262
+ pub .unsubscribe (listener ["handler" ], listener ["topic" ])
263
+ else :
264
+ logger .debug ("Listener not subscribed" , listener )
265
+ except Exception as exc :
266
+ print (f"Removing webhooks listener failed! { exc !r} occurred." )
267
+ logger .warn (f"Removing webhooks listener failed! { exc !r} occurred." )
268
+
269
+
270
+
271
+ def remove_all_listeners (self , topic : str = None ):
272
+ """Remove all listeners for one or all topics
273
+
274
+ Args:
275
+ ----
276
+ topic : str
277
+ The topic to stop listening for (default is None). Default will cause unsubscribing from all topics.
278
+ """
279
+ # Note advanced use of function can include both listenerFilter and topicFilter for this
280
+ # Add when needed
281
+ try :
282
+ pub .unsubAll (topicName = topic )
283
+ except Exception as exc :
284
+ print (f"Removing all webhooks listeners failed! { exc !r} occurred." )
285
+ logger .warning (f"Removing all webhooks listeners failed! { exc !r} occurred." )
286
+
287
+
288
+ async def listen_webhooks (self ):
289
+ # self.webhook_listener: AriesWebhookListener = AriesWebhookListener(webhook_host=webhook_host, webhook_port=webhook_port, webhook_base=webhook_base, is_multitenant=is_multitenant)
290
+
291
+ if self .webhook_listener :
292
+ await self .webhook_listener .listen_webhooks ()
293
+
294
+ async def terminate (self ):
295
+ await self .client_session .close ()
296
+ await self .webhook_listener .terminate ()
0 commit comments