From 2517504ee455f0799e6aef9d39a17cca4562eb8c Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 30 Nov 2023 08:53:38 -0800 Subject: [PATCH 1/5] auto-accept on DIDX implicit req + ability to list and delete OOB invitations Signed-off-by: Shaanjot Gill --- .../protocols/didexchange/v1_0/manager.py | 13 +- .../protocols/didexchange/v1_0/routes.py | 6 + .../protocols/out_of_band/v1_0/routes.py | 117 +++++++++++++++++- 3 files changed, 132 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index 1f4bc3a590..288a2b5d85 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -181,6 +181,7 @@ async def create_request_implicit( alias: str = None, goal_code: str = None, goal: str = None, + auto_accept: bool = False, ) -> ConnRecord: """Create and send a request against a public DID only (no explicit invitation). @@ -192,6 +193,7 @@ async def create_request_implicit( use_public_did: use my public DID for this connection goal_code: Optional self-attested code for sharing intent of connection goal: Optional self-attested string for sharing intent of connection + auto_accept: auto-accept a corresponding connection request Returns: The new `ConnRecord` instance @@ -223,7 +225,13 @@ async def create_request_implicit( ) except StorageNotFoundError: pass - + auto_accept = bool( + auto_accept + or ( + auto_accept is None + and self.profile.settings.get("debug.auto_accept_requests") + ) + ) conn_rec = ConnRecord( my_did=my_public_info.did if my_public_info @@ -237,6 +245,9 @@ async def create_request_implicit( alias=alias, their_public_did=their_public_did, connection_protocol=DIDX_PROTO, + accept=ConnRecord.ACCEPT_AUTO + if auto_accept + else ConnRecord.ACCEPT_MANUAL, ) request = await self.create_request( # saves and updates conn_rec conn_rec=conn_rec, diff --git a/aries_cloudagent/protocols/didexchange/v1_0/routes.py b/aries_cloudagent/protocols/didexchange/v1_0/routes.py index abfacda882..0c7b90cd7c 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/routes.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/routes.py @@ -62,6 +62,10 @@ class DIDXCreateRequestImplicitQueryStringSchema(OpenAPISchema): required=False, metadata={"description": "Alias for connection", "example": "Barry"}, ) + auto_accept = fields.Boolean( + required=False, + metadata={"description": "Auto-accept connection (defaults to configuration)"}, + ) my_endpoint = fields.Str( required=False, validate=ENDPOINT_VALIDATE, @@ -260,6 +264,7 @@ async def didx_create_request_implicit(request: web.BaseRequest): use_public_did = json.loads(request.query.get("use_public_did", "null")) goal_code = request.query.get("goal_code") or None goal = request.query.get("goal") or None + auto_accept = json.loads(request.query.get("auto_accept", "null")) profile = context.profile didx_mgr = DIDXManager(profile) @@ -273,6 +278,7 @@ async def didx_create_request_implicit(request: web.BaseRequest): alias=alias, goal_code=goal_code, goal=goal, + auto_accept=auto_accept, ) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py index 4f11ca51ca..be65143626 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py @@ -4,8 +4,13 @@ import logging from aiohttp import web -from aiohttp_apispec import docs, querystring_schema, request_schema, response_schema - +from aiohttp_apispec import ( + docs, + querystring_schema, + request_schema, + match_info_schema, + response_schema, +) from marshmallow import fields, validate from marshmallow.exceptions import ValidationError @@ -19,7 +24,7 @@ from .manager import OutOfBandManager, OutOfBandManagerError from .message_types import SPEC_URI from .messages.invitation import HSProto, InvitationMessage, InvitationMessageSchema -from .models.invitation import InvitationRecordSchema +from .models.invitation import InvitationRecord, InvitationRecordSchema from .models.oob_record import OobRecordSchema LOGGER = logging.getLogger(__name__) @@ -175,6 +180,45 @@ class InvitationReceiveQueryStringSchema(OpenAPISchema): ) +class InvitationRecListSchema(OpenAPISchema): + """Result schema for invitation record list.""" + + results = fields.List( + fields.Nested(InvitationRecordSchema()), + metadata={"description": "List of transaction records"}, + ) + + +class InvitationRecordResponseSchema(OpenAPISchema): + """Response schema for Invitation Record.""" + + +class InvitationRecordMatchInfoSchema(OpenAPISchema): + """Path parameters and validators for request taking invitation record.""" + + invitation_id = fields.Str( + required=True, + validate=UUID4_VALIDATE, + metadata={ + "description": "Invitation Record identifier", + "example": UUID4_EXAMPLE, + }, + ) + + +class InvitationRecordQueryStringSchema(OpenAPISchema): + """Parameters and validators for invitation records request query string.""" + + invitation_id = fields.Str( + required=False, + validate=UUID4_VALIDATE, + metadata={ + "description": "Identifier for invitation record to be retreived", + "example": UUID4_EXAMPLE, + }, + ) + + @docs( tags=["out-of-band"], summary="Create a new connection invitation", @@ -283,6 +327,71 @@ async def invitation_receive(request: web.BaseRequest): return web.json_response(result.serialize()) +@docs( + tags=["out-of-band"], + summary="Retreive a specific invitation or list of all invitations", +) +@querystring_schema(InvitationRecordMatchInfoSchema()) +@response_schema(InvitationRecListSchema(), 200) +async def ret_invitation_list(request: web.BaseRequest): + """Request handler for searching invitation records. + + Args: + request: aiohttp request object + Returns: + The transaction list response + """ + + context: AdminRequestContext = request["context"] + invitation_id = request.query.get("invitation_id") or None + tag_filter = {} + post_filter = {} + + try: + async with context.profile.session() as session: + if not invitation_id: + records = await InvitationRecord.query( + session, tag_filter, post_filter_positive=post_filter, alt=True + ) + results = [record.serialize() for record in records] + else: + record = await InvitationRecord.retrieve_by_id(session, invitation_id) + results = [record.serialize()] + except (StorageError, BaseModelError) as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + + return web.json_response({"results": results}) + + +@docs( + tags=["out-of-band"], + summary="Delete a single invitation" +) +@match_info_schema(InvitationRecordMatchInfoSchema()) +@response_schema(InvitationRecordResponseSchema(), description="") +async def invitation_remove(request: web.BaseRequest): + """Request handler for removing a invitation record. + + Args: + request: aiohttp request object + + """ + context: AdminRequestContext = request["context"] + invitation_id = request.match_info["invitation_id"] + + try: + async with context.profile.session() as session: + invi_rec = await InvitationRecord.retrieve_by_id( + session, invitation_id + ) + await invi_rec.delete_record(session) + except StorageNotFoundError as err: + raise web.HTTPNotFound(reason=err.roll_up) from err + except StorageError as err: + raise web.HTTPBadRequest(reason=err.roll_up) from err + + return web.json_response({}) + async def register(app: web.Application): """Register routes.""" @@ -290,6 +399,8 @@ async def register(app: web.Application): [ web.post("/out-of-band/create-invitation", invitation_create), web.post("/out-of-band/receive-invitation", invitation_receive), + web.get("/out-of-band/invitations", ret_invitation_list), + web.delete("/out-of-band/invitations/{invitation_id}", invitation_remove), ] ) From 75b3d17c49f795f963149e984dd2dd9b36c18fb5 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 30 Nov 2023 09:01:45 -0800 Subject: [PATCH 2/5] black fmt changes Signed-off-by: Shaanjot Gill --- aries_cloudagent/protocols/didexchange/v1_0/manager.py | 4 +--- aries_cloudagent/protocols/out_of_band/v1_0/routes.py | 10 +++------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index 288a2b5d85..e5caf2dfad 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -245,9 +245,7 @@ async def create_request_implicit( alias=alias, their_public_did=their_public_did, connection_protocol=DIDX_PROTO, - accept=ConnRecord.ACCEPT_AUTO - if auto_accept - else ConnRecord.ACCEPT_MANUAL, + accept=ConnRecord.ACCEPT_AUTO if auto_accept else ConnRecord.ACCEPT_MANUAL, ) request = await self.create_request( # saves and updates conn_rec conn_rec=conn_rec, diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py index be65143626..afc2288fae 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py @@ -327,6 +327,7 @@ async def invitation_receive(request: web.BaseRequest): return web.json_response(result.serialize()) + @docs( tags=["out-of-band"], summary="Retreive a specific invitation or list of all invitations", @@ -363,10 +364,7 @@ async def ret_invitation_list(request: web.BaseRequest): return web.json_response({"results": results}) -@docs( - tags=["out-of-band"], - summary="Delete a single invitation" -) +@docs(tags=["out-of-band"], summary="Delete a single invitation") @match_info_schema(InvitationRecordMatchInfoSchema()) @response_schema(InvitationRecordResponseSchema(), description="") async def invitation_remove(request: web.BaseRequest): @@ -381,9 +379,7 @@ async def invitation_remove(request: web.BaseRequest): try: async with context.profile.session() as session: - invi_rec = await InvitationRecord.retrieve_by_id( - session, invitation_id - ) + invi_rec = await InvitationRecord.retrieve_by_id(session, invitation_id) await invi_rec.delete_record(session) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err From 3de38d1ad3706f59e5b3bb2f8e301e5fa237463e Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 30 Nov 2023 09:18:03 -0800 Subject: [PATCH 3/5] bug fix Signed-off-by: Shaanjot Gill --- aries_cloudagent/protocols/didexchange/v1_0/manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/manager.py b/aries_cloudagent/protocols/didexchange/v1_0/manager.py index e5caf2dfad..4de5712624 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/manager.py @@ -241,7 +241,6 @@ async def create_request_implicit( their_role=ConnRecord.Role.RESPONDER.rfc23, invitation_key=None, invitation_msg_id=None, - accept=None, alias=alias, their_public_did=their_public_did, connection_protocol=DIDX_PROTO, From cf34fc87464aac1ec25fa2272f56d9eaeeb3672f Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 30 Nov 2023 11:04:00 -0800 Subject: [PATCH 4/5] unit test updates Signed-off-by: Shaanjot Gill --- .../didexchange/v1_0/tests/test_manager.py | 1 + .../didexchange/v1_0/tests/test_routes.py | 2 + .../out_of_band/v1_0/tests/test_routes.py | 60 ++++++++++++++++++- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py index d6511ee48a..32b92585dd 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_manager.py @@ -311,6 +311,7 @@ async def test_create_request_implicit_use_public_did(self): mediation_id=None, use_public_did=True, alias="Tester", + auto_accept=True, ) assert info_public.did == conn_rec.my_did diff --git a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_routes.py index 6a3926b5b3..2888c91166 100644 --- a/aries_cloudagent/protocols/didexchange/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/didexchange/v1_0/tests/test_routes.py @@ -101,6 +101,7 @@ async def test_didx_create_request_implicit_not_found_x(self): "my_label": "label baby junior", "my_endpoint": "http://endpoint.ca", "mediator_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "auto_accept": "true", } with mock.patch.object( @@ -121,6 +122,7 @@ async def test_didx_create_request_implicit_wallet_x(self): "their_public_did": "public-did", "my_label": "label baby junior", "my_endpoint": "http://endpoint.ca", + "auto_accept": "true", } with mock.patch.object( diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py index 22bcd06226..ebf7a70927 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py @@ -3,14 +3,20 @@ from .....admin.request_context import AdminRequestContext from .....connections.models.conn_record import ConnRecord +from .....core.in_memory import InMemoryProfile + +from ....didcomm_prefix import DIDCommPrefix +from ....didexchange.v1_0.message_types import ARIES_PROTOCOL as DIDX_PROTO + +from ..models.invitation import InvitationRecord, InvitationMessage from .. import routes as test_module class TestOutOfBandRoutes(IsolatedAsyncioTestCase): async def asyncSetUp(self): - self.session_inject = {} - self.context = AdminRequestContext.test_context(self.session_inject) + self.profile = InMemoryProfile.test_profile() + self.context = AdminRequestContext.test_context(profile=self.profile) self.request_dict = { "context": self.context, "outbound_message_router": mock.CoroutineMock(), @@ -64,6 +70,56 @@ async def test_invitation_create(self): ) mock_json_response.assert_called_once_with({"abc": "123"}) + async def test_ret_invitations_single_item(self): + invi = InvitationMessage( + comment="Hello", + label="A label", + handshake_protocols=[DIDCommPrefix.qualify_current(DIDX_PROTO)], + services=["did:sov:55GkHamhTU1ZbTbV2ab9DE"], + ) + invi_rec_1 = InvitationRecord( + state=InvitationRecord.STATE_AWAIT_RESPONSE, + invitation=invi.serialize(), + ) + await invi_rec_1.save(await self.profile.session()) + invi_rec_2 = InvitationRecord( + state=InvitationRecord.STATE_AWAIT_RESPONSE, + invitation=invi.serialize(), + ) + await invi_rec_2.save(await self.profile.session()) + self.request.query = { + "invitation_id": invi_rec_1.invitation_id, + } + with mock.patch.object( + test_module.web, "json_response", mock.Mock() + ) as mock_json_response: + result = await test_module.ret_invitation_list(self.request) + mock_json_response.assert_called_once() + + async def test_ret_invitations_list(self): + invi = InvitationMessage( + comment="Hello", + label="A label", + handshake_protocols=[DIDCommPrefix.qualify_current(DIDX_PROTO)], + services=["did:sov:55GkHamhTU1ZbTbV2ab9DE"], + ) + invi_rec_1 = InvitationRecord( + state=InvitationRecord.STATE_AWAIT_RESPONSE, + invitation=invi.serialize(), + ) + await invi_rec_1.save(await self.profile.session()) + invi_rec_2 = InvitationRecord( + state=InvitationRecord.STATE_AWAIT_RESPONSE, + invitation=invi.serialize(), + ) + await invi_rec_2.save(await self.profile.session()) + self.request.query = {} + with mock.patch.object( + test_module.web, "json_response", mock.Mock() + ) as mock_json_response: + result = await test_module.ret_invitation_list(self.request) + mock_json_response.assert_called_once() + async def test_invitation_create_with_accept(self): self.request.query = { "multi_use": "true", From eee9ec62a1b70e6af6c1356730f2cb4ca1eef09c Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Thu, 30 Nov 2023 13:41:07 -0800 Subject: [PATCH 5/5] fixes Signed-off-by: Shaanjot Gill --- .../protocols/out_of_band/v1_0/manager.py | 22 +++++ .../out_of_band/v1_0/models/oob_record.py | 1 + .../protocols/out_of_band/v1_0/routes.py | 80 +++---------------- .../out_of_band/v1_0/tests/test_manager.py | 37 +++++++++ .../out_of_band/v1_0/tests/test_routes.py | 61 +++----------- aries_cloudagent/resolver/default/peer3.py | 6 +- 6 files changed, 84 insertions(+), 123 deletions(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 649401a5c5..7ddbbdc847 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -947,6 +947,28 @@ async def delete_stale_connection_by_invitation(self, invi_msg_id: str): for conn_rec in conn_records: await conn_rec.delete_record(session) + async def delete_conn_and_oob_record_invitation(self, invi_msg_id: str): + """Delete conn_record and oob_record associated with an invi_msg_id.""" + async with self.profile.session() as session: + conn_records = await ConnRecord.query( + session, + tag_filter={ + "invitation_msg_id": invi_msg_id, + }, + post_filter_positive={}, + ) + for conn_rec in conn_records: + await conn_rec.delete_record(session) + oob_records = await OobRecord.query( + session, + tag_filter={ + "invi_msg_id": invi_msg_id, + }, + post_filter_positive={}, + ) + for oob_rec in oob_records: + await oob_rec.delete_record(session) + async def receive_reuse_message( self, reuse_msg: HandshakeReuse, diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py index 5a3ff8d84a..3451ab14c9 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/models/oob_record.py @@ -110,6 +110,7 @@ def record_value(self) -> dict: "connection_id", "role", "our_service", + "invi_msg_id", ) }, **{ diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py index afc2288fae..e50732ccab 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/routes.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/routes.py @@ -24,7 +24,7 @@ from .manager import OutOfBandManager, OutOfBandManagerError from .message_types import SPEC_URI from .messages.invitation import HSProto, InvitationMessage, InvitationMessageSchema -from .models.invitation import InvitationRecord, InvitationRecordSchema +from .models.invitation import InvitationRecordSchema from .models.oob_record import OobRecordSchema LOGGER = logging.getLogger(__name__) @@ -180,15 +180,6 @@ class InvitationReceiveQueryStringSchema(OpenAPISchema): ) -class InvitationRecListSchema(OpenAPISchema): - """Result schema for invitation record list.""" - - results = fields.List( - fields.Nested(InvitationRecordSchema()), - metadata={"description": "List of transaction records"}, - ) - - class InvitationRecordResponseSchema(OpenAPISchema): """Response schema for Invitation Record.""" @@ -196,24 +187,11 @@ class InvitationRecordResponseSchema(OpenAPISchema): class InvitationRecordMatchInfoSchema(OpenAPISchema): """Path parameters and validators for request taking invitation record.""" - invitation_id = fields.Str( + invi_msg_id = fields.Str( required=True, validate=UUID4_VALIDATE, metadata={ - "description": "Invitation Record identifier", - "example": UUID4_EXAMPLE, - }, - ) - - -class InvitationRecordQueryStringSchema(OpenAPISchema): - """Parameters and validators for invitation records request query string.""" - - invitation_id = fields.Str( - required=False, - validate=UUID4_VALIDATE, - metadata={ - "description": "Identifier for invitation record to be retreived", + "description": "Invitation Message identifier", "example": UUID4_EXAMPLE, }, ) @@ -328,59 +306,22 @@ async def invitation_receive(request: web.BaseRequest): return web.json_response(result.serialize()) -@docs( - tags=["out-of-band"], - summary="Retreive a specific invitation or list of all invitations", -) -@querystring_schema(InvitationRecordMatchInfoSchema()) -@response_schema(InvitationRecListSchema(), 200) -async def ret_invitation_list(request: web.BaseRequest): - """Request handler for searching invitation records. - - Args: - request: aiohttp request object - Returns: - The transaction list response - """ - - context: AdminRequestContext = request["context"] - invitation_id = request.query.get("invitation_id") or None - tag_filter = {} - post_filter = {} - - try: - async with context.profile.session() as session: - if not invitation_id: - records = await InvitationRecord.query( - session, tag_filter, post_filter_positive=post_filter, alt=True - ) - results = [record.serialize() for record in records] - else: - record = await InvitationRecord.retrieve_by_id(session, invitation_id) - results = [record.serialize()] - except (StorageError, BaseModelError) as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err - - return web.json_response({"results": results}) - - -@docs(tags=["out-of-band"], summary="Delete a single invitation") +@docs(tags=["out-of-band"], summary="Delete records associated with invitation") @match_info_schema(InvitationRecordMatchInfoSchema()) @response_schema(InvitationRecordResponseSchema(), description="") async def invitation_remove(request: web.BaseRequest): - """Request handler for removing a invitation record. + """Request handler for removing a invitation related conn and oob records. Args: request: aiohttp request object """ context: AdminRequestContext = request["context"] - invitation_id = request.match_info["invitation_id"] - + invi_msg_id = request.match_info["invi_msg_id"] + profile = context.profile + oob_mgr = OutOfBandManager(profile) try: - async with context.profile.session() as session: - invi_rec = await InvitationRecord.retrieve_by_id(session, invitation_id) - await invi_rec.delete_record(session) + await oob_mgr.delete_conn_and_oob_record_invitation(invi_msg_id) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err except StorageError as err: @@ -395,8 +336,7 @@ async def register(app: web.Application): [ web.post("/out-of-band/create-invitation", invitation_create), web.post("/out-of-band/receive-invitation", invitation_receive), - web.get("/out-of-band/invitations", ret_invitation_list), - web.delete("/out-of-band/invitations/{invitation_id}", invitation_remove), + web.delete("/out-of-band/invitations/{invi_msg_id}", invitation_remove), ] ) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py index b8a33c2598..745f51c42f 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_manager.py @@ -1844,3 +1844,40 @@ async def test_delete_stale_connection_by_invitation(self): mock_connrecord_query.return_value = records await self.manager.delete_stale_connection_by_invitation("test123") mock_connrecord_delete.assert_called_once() + + async def test_delete_conn_and_oob_record_invitation(self): + invitation = InvitationMessage() + oob_records = [ + OobRecord( + invitation=invitation, + invi_msg_id=invitation._id, + role=OobRecord.ROLE_RECEIVER, + connection_id=self.test_conn_rec.connection_id, + state=OobRecord.STATE_INITIAL, + ) + ] + conn_records = [ + ConnRecord( + my_did=self.test_did, + their_did="FBmi5JLf5g58kDnNXMy4QM", + their_role=ConnRecord.Role.RESPONDER.rfc160, + state=ConnRecord.State.INVITATION.rfc160, + invitation_key="dummy2", + invitation_mode="once", + invitation_msg_id="test123", + ) + ] + with mock.patch.object( + ConnRecord, "query", mock.CoroutineMock() + ) as mock_connrecord_query, mock.patch.object( + ConnRecord, "delete_record", mock.CoroutineMock() + ) as mock_connrecord_delete, mock.patch.object( + OobRecord, "query", mock.CoroutineMock() + ) as mock_oobrecord_query, mock.patch.object( + OobRecord, "delete_record", mock.CoroutineMock() + ) as mock_oobrecord_delete: + mock_connrecord_query.return_value = conn_records + mock_oobrecord_query.return_value = oob_records + await self.manager.delete_conn_and_oob_record_invitation("test123") + mock_connrecord_delete.assert_called_once() + mock_oobrecord_delete.assert_called_once() diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py index ebf7a70927..ecf22baa31 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py @@ -5,11 +5,6 @@ from .....connections.models.conn_record import ConnRecord from .....core.in_memory import InMemoryProfile -from ....didcomm_prefix import DIDCommPrefix -from ....didexchange.v1_0.message_types import ARIES_PROTOCOL as DIDX_PROTO - -from ..models.invitation import InvitationRecord, InvitationMessage - from .. import routes as test_module @@ -70,55 +65,19 @@ async def test_invitation_create(self): ) mock_json_response.assert_called_once_with({"abc": "123"}) - async def test_ret_invitations_single_item(self): - invi = InvitationMessage( - comment="Hello", - label="A label", - handshake_protocols=[DIDCommPrefix.qualify_current(DIDX_PROTO)], - services=["did:sov:55GkHamhTU1ZbTbV2ab9DE"], - ) - invi_rec_1 = InvitationRecord( - state=InvitationRecord.STATE_AWAIT_RESPONSE, - invitation=invi.serialize(), - ) - await invi_rec_1.save(await self.profile.session()) - invi_rec_2 = InvitationRecord( - state=InvitationRecord.STATE_AWAIT_RESPONSE, - invitation=invi.serialize(), - ) - await invi_rec_2.save(await self.profile.session()) - self.request.query = { - "invitation_id": invi_rec_1.invitation_id, - } - with mock.patch.object( - test_module.web, "json_response", mock.Mock() - ) as mock_json_response: - result = await test_module.ret_invitation_list(self.request) - mock_json_response.assert_called_once() - - async def test_ret_invitations_list(self): - invi = InvitationMessage( - comment="Hello", - label="A label", - handshake_protocols=[DIDCommPrefix.qualify_current(DIDX_PROTO)], - services=["did:sov:55GkHamhTU1ZbTbV2ab9DE"], - ) - invi_rec_1 = InvitationRecord( - state=InvitationRecord.STATE_AWAIT_RESPONSE, - invitation=invi.serialize(), - ) - await invi_rec_1.save(await self.profile.session()) - invi_rec_2 = InvitationRecord( - state=InvitationRecord.STATE_AWAIT_RESPONSE, - invitation=invi.serialize(), - ) - await invi_rec_2.save(await self.profile.session()) - self.request.query = {} + async def test_invitation_remove(self): + self.request.match_info = {"invi_msg_id": "dummy"} + with mock.patch.object( + test_module, "OutOfBandManager", autospec=True + ) as mock_oob_mgr, mock.patch.object( test_module.web, "json_response", mock.Mock() ) as mock_json_response: - result = await test_module.ret_invitation_list(self.request) - mock_json_response.assert_called_once() + mock_oob_mgr.return_value.delete_conn_and_oob_record_invitation = ( + mock.CoroutineMock(return_value=None) + ) + await test_module.invitation_remove(self.request) + mock_json_response.assert_called_once_with({}) async def test_invitation_create_with_accept(self): self.request.query = { diff --git a/aries_cloudagent/resolver/default/peer3.py b/aries_cloudagent/resolver/default/peer3.py index 5a3100d95d..bca776e449 100644 --- a/aries_cloudagent/resolver/default/peer3.py +++ b/aries_cloudagent/resolver/default/peer3.py @@ -84,8 +84,10 @@ async def create_and_store(self, profile: Profile, peer2: str): async def remove_record_for_deleted_conn(self, profile: Profile, event: Event): """Remove record for deleted connection, if found.""" - their_did = event.payload["their_did"] - my_did = event.payload["my_did"] + their_did = event.payload.get("their_did") + my_did = event.payload.get("my_did") + if not their_did and not my_did: + return dids = [ *(did for did in (their_did, my_did) if PEER3_PATTERN.match(did)), *(peer2to3(did) for did in (their_did, my_did) if PEER2_PATTERN.match(did)),