Skip to content

Commit 664bdb5

Browse files
authored
Merge pull request #77 from didx-xyz/improvement-controller-session
Improvement controller session
2 parents 4cb7c87 + 82a8788 commit 664bdb5

File tree

6 files changed

+197
-98
lines changed

6 files changed

+197
-98
lines changed

libs/aries-basic-controller/aries_basic_controller/aries_controller.py

Lines changed: 179 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
ClientSession,
44
ClientRequest,
55
)
6+
from dataclasses import dataclass
67
from pubsub import pub
78
import sys
89

@@ -26,51 +27,84 @@
2627

2728
logger = logging.getLogger("aries_controller")
2829

29-
class AriesAgentController:
3030

31+
@dataclass
32+
class AriesAgentController:
33+
"""The Aries Agent Controller class
34+
35+
This class allows you to interact with Aries by exposing the aca-py API.
36+
37+
Attributes
38+
----------
39+
webhook_host : str
40+
The url of the webhook host
41+
webhook_port : int
42+
The exposed port for webhooks on the host
43+
admin_url : str
44+
The URL for the Admin API
45+
webhook_base : str
46+
The base url for webhooks (default is "")
47+
connections : bool
48+
Specify whether to create connecitons (default is True)
49+
messaging : bool
50+
Initialise the messaging interface (default is True)
51+
multitenant : bool
52+
Initialise the multitenant interface (default is False)
53+
mediation : bool
54+
Initialise the mediation interface (default is False)
55+
issuer : bool
56+
Initialise the issuer interface (defautl is True)
57+
action_menu : bool
58+
Initialise the action menu interface (default is True)
59+
revocations : bool
60+
Initialise revocation interface for credentials (default is True)
61+
api_key : str
62+
The API key (default is None)
63+
tenant_jwt: str
64+
The tenant JW token (default is None)
65+
"""
66+
3167
## TODO rethink how to initialise. Too many args?
3268
## is it important to let users config connections/issuer etc
33-
def __init__(
34-
self,
35-
webhook_host: str,
36-
webhook_port: int,
37-
admin_url: str,
38-
webhook_base: str = "",
39-
connections: bool = True,
40-
messaging: bool = True,
41-
multitenant: bool = False,
42-
mediation: bool = False,
43-
issuer: bool = True,
44-
action_menu: bool = True,
45-
revocations: bool = True,
46-
api_key: str = None,
47-
tennant_jwt: str = None,
48-
):
49-
69+
webhook_host: str
70+
webhook_port: int
71+
admin_url: str
72+
webhook_base: str = ""
73+
connections: bool = True
74+
messaging: bool = True
75+
multitenant: bool = False
76+
mediation: bool = False
77+
issuer: bool = True
78+
action_menu: bool = True
79+
revocations: bool = True
80+
api_key: str = None
81+
tenant_jwt: str = None
82+
83+
84+
def __post_init__(self):
85+
"""Constructs additional attributes,
86+
and logic defined by attributes set during initial instantiation
87+
"""
88+
5089
self.webhook_site = None
51-
self.admin_url = admin_url
52-
if webhook_base:
53-
self.webhook_base = webhook_base
54-
else:
55-
self.webhook_base = ""
56-
self.webhook_host = webhook_host
57-
self.webhook_port = webhook_port
5890
self.connections_controller = None
59-
60-
headers = {}
61-
62-
if api_key:
63-
headers.update({"X-API-Key": api_key})
64-
65-
if tennant_jwt:
66-
headers.update({'Authorization': 'Bearer ' + tennant_jwt, 'content-type': "application/json"})
67-
68-
self.client_session: ClientSession = ClientSession(headers=headers)
6991

92+
# Construct headers for Client Session and the session itself
93+
self.headers = {}
94+
95+
if self.api_key:
96+
self.headers.update({"X-API-Key": self.api_key})
97+
98+
if self.tenant_jwt:
99+
self.headers.update({'Authorization': 'Bearer ' + self.tenant_jwt, 'content-type': "application/json"})
70100

71-
if connections:
101+
self.client_session: ClientSession = ClientSession(headers=self.headers)
102+
103+
# Instantiate controllers based on the provided attributes
104+
if self.connections:
72105
self.connections = ConnectionsController(self.admin_url, self.client_session)
73-
if messaging:
106+
107+
if self.messaging:
74108
self.messaging = MessagingController(self.admin_url, self.client_session)
75109

76110
self.proofs = ProofController(self.admin_url, self.client_session)
@@ -79,50 +113,9 @@ def __init__(
79113
self.server = ServerController(self.admin_url, self.client_session)
80114
self.oob = OOBController(self.admin_url, self.client_session)
81115

82-
if multitenant:
83-
self.multitenant = MultitenancyController(self.admin_url, self.client_session)
84-
85-
if mediation:
86-
self.mediation = MediationController(self.admin_url, self.client_session)
87-
88-
if issuer:
89-
self.schema = SchemaController(self.admin_url, self.client_session)
90-
self.wallet = WalletController(self.admin_url, self.client_session)
91-
self.definitions = DefinitionsController(self.admin_url, self.client_session)
92-
self.issuer = IssuerController(self.admin_url, self.client_session, self.connections,
93-
self.wallet, self.definitions)
94-
95-
if action_menu:
96-
self.action_menu = ActionMenuController(self.admin_url, self.client_session)
97-
98-
if revocations:
99-
self.revocations = RevocationController(
100-
self.admin_url,
101-
self.client_session
102-
)
103-
104-
# TODO: Determine whether we really want to essentially create a whole new ClientSession object as done below
105-
# Ideally we'd just update the existing session along the lines of self.client_session(headers)
106-
# That does not work, though because it is not callable and updating cannot be achieved reliably
107-
# because headers can be of different type
108-
# from https://docs.aiohttp.org/en/stable/client_reference.html :
109-
# "May be either iterable of key-value pairs or Mapping (e.g. dict, CIMultiDict)."
110-
# So for now let's create a new ClientSession and use all the instances current attributes
111-
# to update every attr using ClientSession
112-
def update_tennant_jwt(self, tennant_jwt):
113-
self.tennant_jwt = tennant_jwt
114-
headers = {'Authorization': 'Bearer ' + tennant_jwt, 'content-type': "application/json"}
115-
self.client_session: ClientSession = ClientSession(headers=headers)
116-
117-
if self.connections:
118-
self.connections = ConnectionsController(self.admin_url, self.client_session)
119-
120-
if self.messaging:
121-
self.messaging = MessagingController(self.admin_url, self.client_session)
122-
123116
if self.multitenant:
124117
self.multitenant = MultitenancyController(self.admin_url, self.client_session)
125-
118+
126119
if self.mediation:
127120
self.mediation = MediationController(self.admin_url, self.client_session)
128121

@@ -131,7 +124,7 @@ def update_tennant_jwt(self, tennant_jwt):
131124
self.wallet = WalletController(self.admin_url, self.client_session)
132125
self.definitions = DefinitionsController(self.admin_url, self.client_session)
133126
self.issuer = IssuerController(self.admin_url, self.client_session, self.connections,
134-
self.wallet, self.definitions)
127+
self.wallet, self.definitions)
135128

136129
if self.action_menu:
137130
self.action_menu = ActionMenuController(self.admin_url, self.client_session)
@@ -142,8 +135,63 @@ def update_tennant_jwt(self, tennant_jwt):
142135
self.client_session
143136
)
144137

138+
139+
def update_tenant_jwt(self, tenant_jwt: str):
140+
"""Update the tenant JW token attribute and the header
141+
142+
Args:
143+
----
144+
tenant_jwt : str
145+
The tenant's JW token
146+
"""
147+
self.tenant_jwt = tenant_jwt
148+
self.headers.update({'Authorization': 'Bearer ' + tenant_jwt, 'content-type': "application/json"})
149+
self.client_session.headers.update(self.headers)
150+
151+
152+
def update_api_key(self, api_key: str):
153+
"""Update the API Key attribute and the header
154+
155+
Args:
156+
----
157+
api_key : str
158+
The API Key
159+
"""
160+
self.api_key = api_key
161+
self.headers.update({"X-API-Key": api_key})
162+
self.client_session.headers.update(self.headers)
163+
164+
165+
def remove_api_key(self):
166+
"""Removes the API key attribute and corresponding headers from the Client Session"""
167+
self.api_key = None
168+
if 'X-API-Key' in self.client_session.headers:
169+
del self.client_session.headers['X-API-Key']
170+
del self.headers['X-API-Key']
171+
172+
173+
def remove_tenant_jwt(self):
174+
"""Removes the tenant's JW Token attribute and corresponding headers from the Client Session"""
175+
self.tenant_jwt = None
176+
if 'Authorization' in self.client_session.headers:
177+
del self.client_session.headers['Authorization']
178+
del self.headers['Authorization']
179+
if 'content-type' in self.client_session.headers:
180+
del self.client_session.headers['content-type']
181+
del self.headers['content-type']
182+
145183

146184
def register_listeners(self, listeners, defaults=True):
185+
"""Registers the webhook listners
186+
187+
Args:
188+
----
189+
listeners : [dict]
190+
A collection of dictionaries comprised of a "handler": handler (fct) and a "topic":"topicname" key-value pairs
191+
defaults : bool
192+
Whether to connect to the default handlers for connections, basicmessage and present_proof
193+
(default is True)
194+
"""
147195
try:
148196
if defaults:
149197
if self.connections:
@@ -160,15 +208,31 @@ def register_listeners(self, listeners, defaults=True):
160208
logger.warn(f"Register webhooks listeners failed! {exc!r} occurred.")
161209

162210

211+
163212
def add_listener(self, listener):
213+
"""Subscribe to a listeners for a topic
214+
215+
Args:
216+
----
217+
listener : dict
218+
A dictionary comprised of a "handler": handler (fct) and a "topic":"topicname" key-value pairs
219+
"""
164220
try:
165221
pub.subscribe(listener["handler"], listener["topic"])
166222
except Exception as exc:
167223
print(f"Adding webhooks listener failed! {exc!r} occurred.")
168224
logger.warn(f"Adding webhooks listener failed! {exc!r} occurred.")
169225

170226

227+
171228
def remove_listener(self, listener):
229+
"""Remove a listener for a topic
230+
231+
Args:
232+
----
233+
listener : dict
234+
A dictionary comprised of a "handler": handler (fct) and a "topic":"topicname" key-value pairs
235+
"""
172236
try:
173237
if pub.isSubscribed(listener["handler"], listener["topic"]):
174238
pub.unsubscribe(listener["handler"], listener["topic"])
@@ -179,7 +243,15 @@ def remove_listener(self, listener):
179243
logger.warn(f"Removing webhooks listener failed! {exc!r} occurred.")
180244

181245

246+
182247
def remove_all_listeners(self, topic: str = None):
248+
"""Remove all listeners for one or all topics
249+
250+
Args:
251+
----
252+
topic : str
253+
The topic to stop listening for (default is None). Default will cause unsubscribing from all topics.
254+
"""
183255
# Note advanced use of function can include both listenerFilter and topicFilter for this
184256
# Add when needed
185257
try:
@@ -189,7 +261,9 @@ def remove_all_listeners(self, topic: str = None):
189261
logger.warn(f"Removing all webhooks listeners failed! {exc!r} occurred.")
190262

191263

264+
192265
async def listen_webhooks(self):
266+
"""Create a server to listen to webhooks"""
193267
try:
194268
app = web.Application()
195269
app.add_routes([web.post(self.webhook_base + "/topic/{topic}/", self._receive_webhook)])
@@ -202,7 +276,20 @@ async def listen_webhooks(self):
202276
logger.warn(f"Listening webhooks failed! {exc!r} occurred.")
203277

204278

279+
205280
async def _receive_webhook(self, request: ClientRequest):
281+
"""Helper to receive webhooks by requesting it
282+
283+
Args:
284+
----
285+
request : ClientRequest
286+
The client request to which the corresponding webhooks shall be received
287+
288+
Returns:
289+
-------
290+
Response:
291+
A response with status 200
292+
"""
206293
topic = request.match_info["topic"]
207294
try:
208295
payload = await request.json()
@@ -212,7 +299,17 @@ async def _receive_webhook(self, request: ClientRequest):
212299
logger.warn(f"Receiving webhooks failed! {exc!r} occurred.")
213300

214301

302+
215303
async def _handle_webhook(self, topic, payload):
304+
"""Helper handling a webhook
305+
306+
Args:
307+
----
308+
topic : str
309+
The topic to handle webhooks for
310+
payload : dict
311+
A JSON-like dictionary representation of the payload
312+
"""
216313
try:
217314
logging.debug(f"Handle Webhook - {topic}", payload)
218315
pub.sendMessage(topic, payload=payload)
@@ -221,7 +318,9 @@ async def _handle_webhook(self, topic, payload):
221318
logger.warn(f"Handling webhooks failed! {exc!r} occurred when trying to handle this topic: {topic}")
222319

223320

321+
224322
async def terminate(self):
323+
"""Terminate the controller client session and webhook listeners"""
225324
try:
226325
await self.client_session.close()
227326
if self.webhook_site:

libs/aries-basic-controller/aries_basic_controller/controllers/proof.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,17 @@ async def get_presentation_credentials(self, pres_ex_id, count: int = None, wql_
6767

6868

6969
# Send a problem report for presentation exchange
70-
async def send_problem_report(self, request, pres_ex_id):
70+
async def send_problem_report(self, pres_ex_id, request):
7171
return await self.admin_POST(f"{self.base_url}/records/{pres_ex_id}/problem-report", json_data=request)
7272

7373

7474
# Sends a proof presentation
75-
async def send_presentation(self, request, pres_ex_id):
75+
async def send_presentation(self, pres_ex_id, request):
7676
return await self.admin_POST(f"{self.base_url}/records/{pres_ex_id}/send-presentation", json_data=request)
7777

7878

7979
# Sends a presentation request in reference to a proposal
80-
async def send_request_for_proposal(self, request, pres_ex_id):
80+
async def send_request_for_proposal(self, pres_ex_id, request):
8181
return await self.admin_POST(f"{self.base_url}/records/{pres_ex_id}/send-request", json_data=request)
8282

8383

0 commit comments

Comments
 (0)