Skip to content

Commit ce5a995

Browse files
authored
Merge branch 'main' into fix/return-processing-no-response
2 parents f1a7ba3 + ad1cad3 commit ce5a995

7 files changed

Lines changed: 133 additions & 73 deletions

File tree

aries_cloudagent/admin/server.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"acapy::actionmenu::received": "actionmenu",
5454
"acapy::actionmenu::get-active-menu": "get-active-menu",
5555
"acapy::actionmenu::perform-menu-action": "perform-menu-action",
56+
"acapy::keylist::updated": "keylist",
5657
}
5758

5859

aries_cloudagent/core/event_bus.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
Optional,
1616
Pattern,
1717
TYPE_CHECKING,
18+
Tuple,
1819
)
1920
from functools import partial
2021

@@ -193,7 +194,7 @@ class MockEventBus(EventBus):
193194
def __init__(self):
194195
"""Initialize MockEventBus."""
195196
super().__init__()
196-
self.events = []
197+
self.events: List[Tuple[Profile, Event]] = []
197198

198199
async def notify(self, profile: "Profile", event: Event):
199200
"""Append the event to MockEventBus.events."""

aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_handler.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder):
3232
session, context.connection_record.connection_id
3333
)
3434
response = await mgr.update_keylist(record, updates=context.message.updates)
35+
response.assign_thread_from(context.message)
3536
await responder.send_reply(response)
3637
except (StorageNotFoundError, MediationNotGrantedError):
3738
reply = CMProblemReport(

aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/keylist_update_response_handler.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,6 @@ async def handle(self, context: RequestContext, responder: BaseResponder):
2525
await mgr.store_update_results(
2626
context.connection_record.connection_id, context.message.updated
2727
)
28+
await mgr.notify_keylist_updated(
29+
context.connection_record.connection_id, context.message
30+
)

aries_cloudagent/protocols/coordinate_mediation/v1_0/handlers/tests/test_keylist_update_response_handler.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ async def test_handler(self):
4646
handler, responder = KeylistUpdateResponseHandler(), MockResponder()
4747
with async_mock.patch.object(
4848
MediationManager, "store_update_results"
49-
) as mock_method:
49+
) as mock_store, async_mock.patch.object(
50+
MediationManager, "notify_keylist_updated"
51+
) as mock_notify:
5052
await handler.handle(self.context, responder)
51-
mock_method.assert_called_once_with(TEST_CONN_ID, self.updated)
53+
mock_store.assert_called_once_with(TEST_CONN_ID, self.updated)
54+
mock_notify.assert_called_once_with(TEST_CONN_ID, self.context.message)

aries_cloudagent/protocols/coordinate_mediation/v1_0/manager.py

Lines changed: 68 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class MediationManager:
6060
SET_TO_DEFAULT_ON_GRANTED = "set_to_default_on_granted"
6161
METADATA_KEY = "mediation"
6262
METADATA_ID = "id"
63+
KEYLIST_UPDATED_EVENT = "acapy::keylist::updated"
6364

6465
def __init__(self, profile: Profile):
6566
"""Initialize Mediation Manager.
@@ -534,63 +535,79 @@ async def store_update_results(
534535
session: An active profile session
535536
536537
"""
537-
session = await self._profile.session()
538538
to_save: Sequence[RouteRecord] = []
539539
to_remove: Sequence[RouteRecord] = []
540-
for updated in results:
541-
if updated.result != KeylistUpdated.RESULT_SUCCESS:
542-
# TODO better handle different results?
543-
LOGGER.warning(
544-
"Keylist update failure: %s(%s): %s",
545-
updated.action,
546-
updated.recipient_key,
547-
updated.result,
548-
)
549-
continue
550-
if updated.action == KeylistUpdateRule.RULE_ADD:
551-
# Multi-tenancy uses route record for internal relaying of wallets
552-
# So the record could already exist. We update in that case
553-
try:
554-
record = await RouteRecord.retrieve_by_recipient_key(
555-
session, updated.recipient_key
556-
)
557-
record.connection_id = connection_id
558-
record.role = RouteRecord.ROLE_CLIENT
559-
except StorageNotFoundError:
560-
record = RouteRecord(
561-
role=RouteRecord.ROLE_CLIENT,
562-
recipient_key=updated.recipient_key,
563-
connection_id=connection_id,
564-
)
565-
to_save.append(record)
566-
elif updated.action == KeylistUpdateRule.RULE_REMOVE:
567-
try:
568-
records = await RouteRecord.query(
569-
session,
570-
{
571-
"role": RouteRecord.ROLE_CLIENT,
572-
"connection_id": connection_id,
573-
"recipient_key": updated.recipient_key,
574-
},
575-
)
576-
except StorageNotFoundError as err:
577-
LOGGER.error(
578-
"No route found while processing keylist update response: %s",
579-
err,
540+
541+
async with self._profile.session() as session:
542+
for updated in results:
543+
if updated.result != KeylistUpdated.RESULT_SUCCESS:
544+
# TODO better handle different results?
545+
LOGGER.warning(
546+
"Keylist update failure: %s(%s): %s",
547+
updated.action,
548+
updated.recipient_key,
549+
updated.result,
580550
)
581-
else:
582-
if len(records) > 1:
551+
continue
552+
if updated.action == KeylistUpdateRule.RULE_ADD:
553+
# Multi-tenancy uses route record for internal relaying of wallets
554+
# So the record could already exist. We update in that case
555+
try:
556+
record = await RouteRecord.retrieve_by_recipient_key(
557+
session, updated.recipient_key
558+
)
559+
record.connection_id = connection_id
560+
record.role = RouteRecord.ROLE_CLIENT
561+
except StorageNotFoundError:
562+
record = RouteRecord(
563+
role=RouteRecord.ROLE_CLIENT,
564+
recipient_key=updated.recipient_key,
565+
connection_id=connection_id,
566+
)
567+
to_save.append(record)
568+
elif updated.action == KeylistUpdateRule.RULE_REMOVE:
569+
try:
570+
records = await RouteRecord.query(
571+
session,
572+
{
573+
"role": RouteRecord.ROLE_CLIENT,
574+
"connection_id": connection_id,
575+
"recipient_key": updated.recipient_key,
576+
},
577+
)
578+
except StorageNotFoundError as err:
583579
LOGGER.error(
584-
f"Too many ({len(records)}) routes found "
585-
"while processing keylist update response"
580+
"No route found while processing keylist update response: %s",
581+
err,
586582
)
587-
record = records[0]
588-
to_remove.append(record)
583+
else:
584+
if len(records) > 1:
585+
LOGGER.error(
586+
f"Too many ({len(records)}) routes found "
587+
"while processing keylist update response"
588+
)
589+
record = records[0]
590+
to_remove.append(record)
591+
592+
for record_for_saving in to_save:
593+
await record_for_saving.save(
594+
session, reason="Route successfully added."
595+
)
596+
for record_for_removal in to_remove:
597+
await record_for_removal.delete_record(session)
589598

590-
for record_for_saving in to_save:
591-
await record_for_saving.save(session, reason="Route successfully added.")
592-
for record_for_removal in to_remove:
593-
await record_for_removal.delete_record(session)
599+
async def notify_keylist_updated(
600+
self, connection_id: str, response: KeylistUpdateResponse
601+
):
602+
"""Notify of keylist update response received."""
603+
await self._profile.notify(
604+
self.KEYLIST_UPDATED_EVENT,
605+
{
606+
"connection_id": connection_id,
607+
"thread_id": response._thread_id,
608+
"updated": [update.serialize() for update in response.updated],
609+
},
610+
)
594611

595612
async def get_my_keylist(
596613
self, connection_id: Optional[str] = None

aries_cloudagent/protocols/coordinate_mediation/v1_0/tests/test_mediation_manager.py

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
"""Test MediationManager."""
22
import logging
3-
4-
import pytest
3+
from typing import AsyncIterable, Iterable
54

65
from asynctest import mock as async_mock
6+
import pytest
77

8+
from .. import manager as test_module
9+
from .....core.event_bus import EventBus, MockEventBus
10+
from .....core.in_memory import InMemoryProfile
811
from .....core.profile import Profile, ProfileSession
9-
from .....connections.models.conn_record import ConnRecord
10-
from .....messaging.request_context import RequestContext
1112
from .....storage.error import StorageNotFoundError
12-
from .....transport.inbound.receipt import MessageReceipt
13-
1413
from ....routing.v1_0.models.route_record import RouteRecord
15-
16-
from .. import manager as test_module
1714
from ..manager import (
1815
MediationAlreadyExists,
1916
MediationManager,
@@ -22,12 +19,14 @@
2219
)
2320
from ..messages.inner.keylist_update_rule import KeylistUpdateRule
2421
from ..messages.inner.keylist_updated import KeylistUpdated
22+
from ..messages.keylist_update_response import KeylistUpdateResponse
2523
from ..messages.mediate_deny import MediationDeny
2624
from ..messages.mediate_grant import MediationGrant
2725
from ..messages.mediate_request import MediationRequest
2826
from ..models.mediation_record import MediationRecord
2927

3028
TEST_CONN_ID = "conn-id"
29+
TEST_THREAD_ID = "thread-id"
3130
TEST_ENDPOINT = "https://example.com"
3231
TEST_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx"
3332
TEST_ROUTE_VERKEY = "9WCgWKUaAJj3VWxxtzvvMQN3AoFxoBtBDo9ntwJnVVCC"
@@ -36,29 +35,32 @@
3635

3736

3837
@pytest.fixture
39-
async def profile() -> Profile:
38+
def profile() -> Iterable[Profile]:
4039
"""Fixture for profile used in tests."""
4140
# pylint: disable=W0621
42-
context = RequestContext.test_context()
43-
context.message_receipt = MessageReceipt(sender_verkey=TEST_VERKEY)
44-
context.connection_record = ConnRecord(connection_id=TEST_CONN_ID)
45-
yield context.profile
41+
yield InMemoryProfile.test_profile(bind={EventBus: MockEventBus()})
42+
43+
44+
@pytest.fixture
45+
def mock_event_bus(profile: Profile):
46+
yield profile.inject(EventBus)
4647

4748

4849
@pytest.fixture
49-
async def session(profile) -> ProfileSession: # pylint: disable=W0621
50+
async def session(profile) -> AsyncIterable[ProfileSession]: # pylint: disable=W0621
5051
"""Fixture for profile session used in tests."""
51-
yield await profile.session()
52+
async with profile.session() as session:
53+
yield session
5254

5355

5456
@pytest.fixture
55-
async def manager(profile) -> MediationManager: # pylint: disable=W0621
57+
def manager(profile) -> Iterable[MediationManager]: # pylint: disable=W0621
5658
"""Fixture for manager used in tests."""
5759
yield MediationManager(profile)
5860

5961

6062
@pytest.fixture
61-
def record() -> MediationRecord:
63+
def record() -> Iterable[MediationRecord]:
6264
"""Fixture for record used in tests."""
6365
yield MediationRecord(
6466
state=MediationRecord.STATE_GRANTED, connection_id=TEST_CONN_ID
@@ -71,7 +73,7 @@ class TestMediationManager: # pylint: disable=R0904,W0621
7173
async def test_create_manager_no_profile(self):
7274
"""test_create_manager_no_profile."""
7375
with pytest.raises(MediationManagerError):
74-
await MediationManager(None)
76+
MediationManager(None)
7577

7678
async def test_create_did(self, manager, session):
7779
"""test_create_did."""
@@ -363,7 +365,11 @@ async def test_add_remove_key_mix(self, manager):
363365
assert update.updates[0].recipient_key == TEST_VERKEY
364366
assert update.updates[1].recipient_key == TEST_ROUTE_VERKEY
365367

366-
async def test_store_update_results(self, session, manager):
368+
async def test_store_update_results(
369+
self,
370+
session: ProfileSession,
371+
manager: MediationManager,
372+
):
367373
"""test_store_update_results."""
368374
await RouteRecord(
369375
role=RouteRecord.ROLE_CLIENT,
@@ -467,6 +473,34 @@ async def test_store_update_results_errors(self, caplog, manager):
467473
assert "server_error" in caplog.text
468474
print(caplog.text)
469475

476+
async def test_notify_keylist_updated(
477+
self, manager: MediationManager, mock_event_bus: MockEventBus
478+
):
479+
"""test notify_keylist_updated."""
480+
response = KeylistUpdateResponse(
481+
updated=[
482+
KeylistUpdated(
483+
recipient_key=TEST_ROUTE_VERKEY,
484+
action=KeylistUpdateRule.RULE_ADD,
485+
result=KeylistUpdated.RESULT_SUCCESS,
486+
),
487+
KeylistUpdated(
488+
recipient_key=TEST_VERKEY,
489+
action=KeylistUpdateRule.RULE_REMOVE,
490+
result=KeylistUpdated.RESULT_SUCCESS,
491+
),
492+
],
493+
)
494+
response.assign_thread_id(TEST_THREAD_ID)
495+
await manager.notify_keylist_updated(TEST_CONN_ID, response)
496+
assert mock_event_bus.events
497+
assert mock_event_bus.events[0][1].topic == manager.KEYLIST_UPDATED_EVENT
498+
assert mock_event_bus.events[0][1].payload == {
499+
"connection_id": TEST_CONN_ID,
500+
"thread_id": TEST_THREAD_ID,
501+
"updated": [result.serialize() for result in response.updated],
502+
}
503+
470504
async def test_get_my_keylist(self, session, manager):
471505
"""test_get_my_keylist."""
472506
await RouteRecord(

0 commit comments

Comments
 (0)