From d9d90a19030a643df2265f57c565d1469310c79e Mon Sep 17 00:00:00 2001 From: Harsh Multani Date: Sun, 2 May 2021 23:59:45 +0530 Subject: [PATCH 1/6] Code to allow endorser to write the transaction to ledger, and code to save record in author's wallet. Signed-off-by: Harsh Multani --- .../transaction_acknowledgement_handler.py | 41 ++++ .../endorse_transaction/v1_0/manager.py | 177 +++++++++++++++++- .../endorse_transaction/v1_0/message_types.py | 5 + .../messages/transaction_acknowledgement.py | 51 +++++ .../v1_0/models/transaction_record.py | 2 +- .../endorse_transaction/v1_0/routes.py | 39 ++-- .../v1_0/tests/test_manager.py | 4 + .../v1_0/tests/test_routes.py | 4 + 8 files changed, 292 insertions(+), 31 deletions(-) create mode 100644 aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_acknowledgement_handler.py create mode 100644 aries_cloudagent/protocols/endorse_transaction/v1_0/messages/transaction_acknowledgement.py diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_acknowledgement_handler.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_acknowledgement_handler.py new file mode 100644 index 0000000000..5c999661ea --- /dev/null +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/transaction_acknowledgement_handler.py @@ -0,0 +1,41 @@ +"""Transaction acknowledgement message handler.""" + +from .....messaging.base_handler import ( + BaseHandler, + BaseResponder, + HandlerException, + RequestContext, +) + +from ..manager import TransactionManager, TransactionManagerError +from ..messages.transaction_acknowledgement import TransactionAcknowledgement + + +class TransactionAcknowledgementHandler(BaseHandler): + """Message handler class for Acknowledging transaction.""" + + async def handle(self, context: RequestContext, responder: BaseResponder): + """ + Handle transaction acknowledgement message. + + Args: + context: Request context + responder: Responder callback + """ + + self._logger.debug( + f"TransactionAcknowledgementHandler called with context {context}" + ) + assert isinstance(context.message, TransactionAcknowledgement) + + if not context.connection_ready: + raise HandlerException("No connection established") + + profile_session = await context.session() + mgr = TransactionManager(profile_session) + try: + await mgr.receive_transaction_acknowledgement( + context.message, context.connection_record.connection_id + ) + except TransactionManagerError: + self._logger.exception("Error receiving transaction acknowledgement") diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py index bf52e8b3a0..565001e1e9 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py @@ -1,8 +1,10 @@ """Class to manage transactions.""" -from aiohttp import web import logging import uuid +from asyncio import shield +import json +from time import time from .models.transaction_record import TransactionRecord from .messages.transaction_request import TransactionRequest @@ -11,6 +13,7 @@ from .messages.cancel_transaction import CancelTransaction from .messages.transaction_resend import TransactionResend from .messages.transaction_job_to_send import TransactionJobToSend +from .messages.transaction_acknowledgement import TransactionAcknowledgement from ....connections.models.conn_record import ConnRecord from ....transport.inbound.receipt import MessageReceipt @@ -19,6 +22,16 @@ from ....core.error import BaseError from ....core.profile import ProfileSession +from ....ledger.base import BaseLedger + +from ....indy.issuer import IndyIssuerError +from ....ledger.error import LedgerError + +from ....storage.base import StorageRecord +from ....messaging.schemas.util import SCHEMA_SENT_RECORD_TYPE +from ....messaging.credential_definitions.util import CRED_DEF_SENT_RECORD_TYPE +from .transaction_jobs import TransactionJob + class TransactionManagerError(BaseError): """Transaction error.""" @@ -283,7 +296,7 @@ async def complete_transaction(self, transaction: TransactionRecord): """ Complete a transaction. - This is the final state after the received ledger transaction + This is the final state where the received ledger transaction is written to the ledger. Args: @@ -294,11 +307,85 @@ async def complete_transaction(self, transaction: TransactionRecord): """ - transaction.state = TransactionRecord.STATE_TRANSACTION_COMPLETED profile_session = await self.session + transaction.state = TransactionRecord.STATE_TRANSACTION_ACKED + async with profile_session.profile.session() as session: await transaction.save(session, reason="Completed transaction") + connection_id = transaction.connection_id + + async with profile_session.profile.session() as session: + connection_record = await ConnRecord.retrieve_by_id(session, connection_id) + jobs = await connection_record.metadata_get(self._session, "transaction_jobs") + if not jobs: + raise TransactionManagerError( + "The transaction related jobs are not set up in " + "connection metadata for this connection record" + ) + if "transaction_my_job" not in jobs.keys(): + raise TransactionManagerError( + 'The "transaction_my_job" is not set in "transaction_jobs"' + " in connection metadata for this connection record" + ) + if jobs["transaction_my_job"] == TransactionJob.TRANSACTION_AUTHOR.name: + await self.store_record_in_wallet(transaction) + + transaction_acknowledgement_message = TransactionAcknowledgement( + thread_id=transaction._id + ) + + return transaction, transaction_acknowledgement_message + + async def receive_transaction_acknowledgement( + self, response: TransactionAcknowledgement, connection_id: str + ): + """ + Update the transaction record after receiving the transaction acknowledgement. + + Args: + response: The transaction acknowledgement + connection_id: The connection_id related to this Transaction Record + """ + + profile_session = await self.session + async with profile_session.profile.session() as session: + transaction = await TransactionRecord.retrieve_by_connection_and_thread( + session, connection_id, response.thread_id + ) + + if transaction.state != TransactionRecord.STATE_TRANSACTION_ENDORSED: + raise TransactionManagerError( + "Only an endorsed transaction can be written to the ledger." + ) + + transaction.state = TransactionRecord.STATE_TRANSACTION_ACKED + async with profile_session.profile.session() as session: + await transaction.save(session, reason="Received a transaction ack") + + connection_id = transaction.connection_id + + try: + async with profile_session.profile.session() as session: + connection_record = await ConnRecord.retrieve_by_id( + session, connection_id + ) + except StorageNotFoundError as err: + raise TransactionManagerError(err.roll_up) from err + jobs = await connection_record.metadata_get(self._session, "transaction_jobs") + if not jobs: + raise TransactionManagerError( + "The transaction related jobs are not set up in " + "connection metadata for this connection record" + ) + if "transaction_my_job" not in jobs.keys(): + raise TransactionManagerError( + 'The "transaction_my_job" is not set in "transaction_jobs"' + " in connection metadata for this connection record" + ) + if jobs["transaction_my_job"] == TransactionJob.TRANSACTION_AUTHOR.name: + await self.store_record_in_wallet(transaction) + return transaction async def create_refuse_response( @@ -531,7 +618,7 @@ async def set_transaction_their_job( self._session, receipt.sender_did, receipt.recipient_did ) except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err + raise TransactionManagerError(err.roll_up) from err value = await connection.metadata_get(self._session, "transaction_jobs") if value: @@ -541,3 +628,85 @@ async def set_transaction_their_job( await connection.metadata_set( self._session, key="transaction_jobs", value=value ) + + async def store_record_in_wallet(self, transaction: TransactionRecord): + """ + Store record in wallet. + + Args: + transaction: The transaction from which the schema/cred_def + would be stored in wallet. + """ + + ledger_transaction = transaction.messages_attach[0]["data"]["json"] + + ledger = self._session.inject(BaseLedger) + if not ledger: + reason = "No ledger available" + if not self._session.context.settings.get_value("wallet.type"): + reason += ": missing wallet-type?" + raise TransactionManagerError(reason) + + async with ledger: + try: + ledger_response_json = await shield( + ledger.txn_submit(ledger_transaction, sign=False, taa_accept=False) + ) + except (IndyIssuerError, LedgerError) as err: + raise TransactionManagerError(err.roll_up) from err + + ledger_response = json.loads(ledger_response_json) + + # write the wallet non-secrets record + # TODO refactor this code (duplicated from ledger.indy.py) + if ledger_response["result"]["txn"]["type"] == "101": + # schema transaction + schema_id = ledger_response["result"]["txnMetadata"]["txnId"] + schema_id_parts = schema_id.split(":") + public_did = ledger_response["result"]["txn"]["metadata"]["from"] + schema_tags = { + "schema_id": schema_id, + "schema_issuer_did": public_did, + "schema_name": schema_id_parts[-2], + "schema_version": schema_id_parts[-1], + "epoch": str(int(time())), + } + record = StorageRecord(SCHEMA_SENT_RECORD_TYPE, schema_id, schema_tags) + # TODO refactor this code? + async with ledger: + storage = ledger.get_indy_storage() + await storage.add_record(record) + + elif ledger_response["result"]["txn"]["type"] == "102": + # cred def transaction + async with ledger: + try: + schema_seq_no = str(ledger_response["result"]["txn"]["data"]["ref"]) + schema_response = await shield(ledger.get_schema(schema_seq_no)) + except (IndyIssuerError, LedgerError) as err: + raise TransactionManagerError(err.roll_up) from err + + schema_id = schema_response["id"] + schema_id_parts = schema_id.split(":") + public_did = ledger_response["result"]["txn"]["metadata"]["from"] + credential_definition_id = ledger_response["result"]["txnMetadata"]["txnId"] + cred_def_tags = { + "schema_id": schema_id, + "schema_issuer_did": schema_id_parts[0], + "schema_name": schema_id_parts[-2], + "schema_version": schema_id_parts[-1], + "issuer_did": public_did, + "cred_def_id": credential_definition_id, + "epoch": str(int(time())), + } + record = StorageRecord( + CRED_DEF_SENT_RECORD_TYPE, credential_definition_id, cred_def_tags + ) + # TODO refactor this code? + async with ledger: + storage = ledger.get_indy_storage() + await storage.add_record(record) + + else: + # TODO unknown ledger transaction type, just ignore for now ... + pass diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/message_types.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/message_types.py index b8f43fb1ed..ea17bdc9b7 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/message_types.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/message_types.py @@ -10,6 +10,7 @@ CANCEL_TRANSACTION = "transactions/1.0/cancel" TRANSACTION_RESEND = "transactions/1.0/resend" TRANSACTION_JOB_TO_SEND = "transactions/1.0/transaction_my_job" +TRANSACTION_ACKNOWLEDGEMENT = "transactions/1.0/ack" ATTACHED_MESSAGE = "transactions/1.0/message" PROTOCOL_PACKAGE = "aries_cloudagent.protocols.endorse_transaction.v1_0" @@ -36,5 +37,9 @@ TRANSACTION_JOB_TO_SEND: ( f"{PROTOCOL_PACKAGE}.messages.transaction_job_to_send.TransactionJobToSend" ), + TRANSACTION_ACKNOWLEDGEMENT: ( + f"{PROTOCOL_PACKAGE}.messages.transaction_acknowledgement" + ".TransactionAcknowledgement" + ), } ) diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/transaction_acknowledgement.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/transaction_acknowledgement.py new file mode 100644 index 0000000000..ea0e48780e --- /dev/null +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/transaction_acknowledgement.py @@ -0,0 +1,51 @@ +"""Represents a transaction acknowledgement message.""" + +from marshmallow import EXCLUDE, fields + +from .....messaging.valid import UUIDFour +from .....messaging.ack.message import Ack, AckSchema + +from ..message_types import TRANSACTION_ACKNOWLEDGEMENT, PROTOCOL_PACKAGE + +HANDLER_CLASS = ( + f"{PROTOCOL_PACKAGE}.handlers" + ".transaction_acknowledgement_handler.TransactionAcknowledgementHandler" +) + + +class TransactionAcknowledgement(Ack): + """Class representing a transaction acknowledgement message.""" + + class Meta: + """Metadata for a transaction acknowledgement message.""" + + handler_class = HANDLER_CLASS + message_type = TRANSACTION_ACKNOWLEDGEMENT + schema_class = "TransactionAcknowledgementSchema" + + def __init__( + self, + *, + thread_id: str = None, + **kwargs, + ): + """ + Initialize a transaction acknowledgement object. + + Args: + thread_id: Thread id of transaction record + """ + super().__init__(**kwargs) + self.thread_id = thread_id + + +class TransactionAcknowledgementSchema(AckSchema): + """Transaction Acknowledgement schema class.""" + + class Meta: + """Transaction Acknowledgement metadata.""" + + model_class = TransactionAcknowledgement + unknown = EXCLUDE + + thread_id = fields.Str(required=True, example=UUIDFour.EXAMPLE) diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/models/transaction_record.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/models/transaction_record.py index 841071a551..bdc929aa80 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/models/transaction_record.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/models/transaction_record.py @@ -51,7 +51,7 @@ class Meta: STATE_TRANSACTION_RESENT = "transaction_resent" STATE_TRANSACTION_RESENT_RECEIEVED = "transaction_resent_received" STATE_TRANSACTION_CANCELLED = "transaction_cancelled" - STATE_TRANSACTION_COMPLETED = "transaction_completed" + STATE_TRANSACTION_ACKED = "transaction_acked" def __init__( self, diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py index de2b12b7b8..7331a3d48b 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py @@ -1,5 +1,4 @@ """Endorse Transaction handling admin routes.""" -import json from aiohttp import web from aiohttp_apispec import ( @@ -11,19 +10,15 @@ ) from asyncio import shield from marshmallow import fields, validate -from time import time from ....admin.request_context import AdminRequestContext from ....connections.models.conn_record import ConnRecord from ....indy.issuer import IndyIssuerError from ....ledger.base import BaseLedger from ....ledger.error import LedgerError -from ....messaging.credential_definitions.util import CRED_DEF_SENT_RECORD_TYPE from ....messaging.models.openapi import OpenAPISchema -from ....messaging.schemas.util import SCHEMA_SENT_RECORD_TYPE from ....messaging.valid import UUIDFour from ....messaging.models.base import BaseModelError -from ....storage.base import StorageRecord from ....storage.error import StorageError, StorageNotFoundError from ....wallet.base import BaseWallet @@ -704,7 +699,7 @@ async def set_endorser_info(request: web.BaseRequest): @docs( tags=["endorse-transaction"], - summary="For Author to write an endorsed transaction to the ledger", + summary="For Author / Endorser to write an endorsed transaction to the ledger", ) @match_info_schema(TranIdMatchInfoSchema()) @response_schema(TransactionRecordSchema(), 200) @@ -721,6 +716,7 @@ async def transaction_write(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] + outbound_handler = request["outbound_message_router"] transaction_id = request.match_info["tran_id"] try: @@ -728,33 +724,17 @@ async def transaction_write(request: web.BaseRequest): transaction = await TransactionRecord.retrieve_by_id( session, transaction_id ) - connection_record = await ConnRecord.retrieve_by_id( - session, transaction.connection_id - ) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err except BaseModelError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err - session = await context.session() - jobs = await connection_record.metadata_get(session, "transaction_jobs") - if not jobs: - raise web.HTTPForbidden( - reason=( - "The transaction related jobs are not set up in " - "connection metadata for this connection record" - ) - ) - if jobs["transaction_my_job"] != TransactionJob.TRANSACTION_AUTHOR.name: - raise web.HTTPForbidden( - reason="Only a TRANSACTION_AUTHOR can write a transaction to the ledger" - ) - if transaction.state != TransactionRecord.STATE_TRANSACTION_ENDORSED: raise web.HTTPForbidden( reason="Only an endorsed transaction can be written to the ledger" ) + """ ledger_transaction = transaction.messages_attach[0]["data"]["json"] ledger = context.inject(BaseLedger, required=False) @@ -827,16 +807,23 @@ async def transaction_write(request: web.BaseRequest): else: # TODO unknown ledger transaction type, just ignore for now ... pass + """ # update the final transaction status + session = await context.session() transaction_mgr = TransactionManager(session) try: - tx_completed = await transaction_mgr.complete_transaction( - transaction=transaction - ) + ( + tx_completed, + transaction_acknowledgement_message, + ) = await transaction_mgr.complete_transaction(transaction=transaction) except StorageError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err + await outbound_handler( + transaction_acknowledgement_message, connection_id=transaction.connection_id + ) + return web.json_response(tx_completed.serialize()) diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py index 13e57d6043..66bbc77754 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py @@ -358,6 +358,7 @@ async def test_receive_endorse_response(self): transaction_record.messages_attach[0]["data"]["json"] == self.test_signature ) + """ async def test_complete_transaction(self): transaction_record = await self.manager.create_record( messages_attach=self.test_messages_attach, @@ -372,6 +373,7 @@ async def test_complete_transaction(self): save_record.assert_called_once() assert transaction_record.state == TransactionRecord.STATE_TRANSACTION_COMPLETED + """ async def test_create_refuse_response_bad_state(self): transaction_record = await self.manager.create_record( @@ -652,6 +654,7 @@ async def test_set_transaction_their_job(self): for i in range(2): await self.manager.set_transaction_their_job(mock_job, mock_receipt) + """ async def test_set_transaction_their_job_conn_not_found(self): mock_job = async_mock.MagicMock() mock_receipt = async_mock.MagicMock() @@ -663,3 +666,4 @@ async def test_set_transaction_their_job_conn_not_found(self): with self.assertRaises(web.HTTPNotFound): await self.manager.set_transaction_their_job(mock_job, mock_receipt) + """ diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py index ffb082181c..2de5d3416c 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py @@ -1486,6 +1486,7 @@ async def test_set_endorser_info_their_wrong_job_x(self): with self.assertRaises(test_module.web.HTTPForbidden): await test_module.set_endorser_info(self.request) + """ async def test_transaction_write_schema_txn(self): self.request.match_info = {"tran_id": "dummy"} with async_mock.patch.object( @@ -1526,6 +1527,7 @@ async def test_transaction_write_schema_txn(self): await test_module.transaction_write(self.request) mock_response.assert_called_once_with({"...": "..."}) + """ async def test_transaction_write_not_found_x(self): self.request.match_info = {"tran_id": "dummy"} @@ -1622,6 +1624,7 @@ async def test_transaction_write_wrong_state_x(self): with self.assertRaises(test_module.web.HTTPForbidden): await test_module.transaction_write(self.request) + """ async def test_transaction_write_no_ledger_x(self): self.request.match_info = {"tran_id": "dummy"} self.context.injector.clear_binding(BaseLedger) @@ -1787,6 +1790,7 @@ async def test_transaction_write_ledger_cred_def_txn_ledger_get_schema_x(self): with self.assertRaises(test_module.web.HTTPBadRequest): await test_module.transaction_write(self.request) + """ async def test_transaction_write_schema_txn_complete_x(self): self.request.match_info = {"tran_id": "dummy"} From 6fbfbbdcb8c47981bb01b0f9fdbab19afee9761c Mon Sep 17 00:00:00 2001 From: Harsh Multani Date: Fri, 7 May 2021 02:03:49 +0530 Subject: [PATCH 2/6] Fixed Merge Conflicts Signed-off-by: Harsh Multani --- .../endorse_transaction/v1_0/routes.py | 11 ------ .../v1_0/tests/test_routes.py | 39 ------------------- 2 files changed, 50 deletions(-) diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py index 7331a3d48b..39da9932ea 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py @@ -666,13 +666,6 @@ async def set_endorser_info(request: web.BaseRequest): " in connection metadata for this connection record" ) ) - if "transaction_their_job" not in jobs.keys(): - raise web.HTTPForbidden( - reason=( - 'Ask the other agent to set up "transaction_my_job" in ' - '"transaction_jobs" in connection metadata for their connection record' - ) - ) if jobs["transaction_my_job"] != TransactionJob.TRANSACTION_AUTHOR.name: raise web.HTTPForbidden( reason=( @@ -680,10 +673,6 @@ async def set_endorser_info(request: web.BaseRequest): "to metadata of its connection record" ) ) - if jobs["transaction_their_job"] != TransactionJob.TRANSACTION_ENDORSER.name: - raise web.HTTPForbidden( - reason="The Other agent should have job TRANSACTION_ENDORSER" - ) value = await record.metadata_get(session, "endorser_info") if value: value["endorser_did"] = endorser_did diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py index 2de5d3416c..d1f754c7ad 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py @@ -1427,25 +1427,6 @@ async def test_set_endorser_info_no_transaction_my_job_x(self): with self.assertRaises(test_module.web.HTTPForbidden): await test_module.set_endorser_info(self.request) - async def test_set_endorser_info_no_transaction_their_job_x(self): - self.request.match_info = {"conn_id": "dummy"} - self.request.query = {"endorser_did": "did", "endorser_name": "name"} - - with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_conn_rec_retrieve: - mock_conn_rec_retrieve.return_value = async_mock.MagicMock( - metadata_get=async_mock.CoroutineMock( - return_value={ - "transaction_my_job": ( - test_module.TransactionJob.TRANSACTION_AUTHOR.name - ), - } - ) - ) - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.set_endorser_info(self.request) - async def test_set_endorser_info_my_wrong_job_x(self): self.request.match_info = {"conn_id": "dummy"} self.request.query = {"endorser_did": "did", "endorser_name": "name"} @@ -1466,26 +1447,6 @@ async def test_set_endorser_info_my_wrong_job_x(self): with self.assertRaises(test_module.web.HTTPForbidden): await test_module.set_endorser_info(self.request) - async def test_set_endorser_info_their_wrong_job_x(self): - self.request.match_info = {"conn_id": "dummy"} - self.request.query = {"endorser_did": "did", "endorser_name": "name"} - with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_conn_rec_retrieve: - mock_conn_rec_retrieve.return_value = async_mock.MagicMock( - metadata_get=async_mock.CoroutineMock( - return_value={ - "transaction_my_job": ( - test_module.TransactionJob.TRANSACTION_AUTHOR.name - ), - "transaction_their_job": "a suffusion of yellow", - } - ) - ) - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.set_endorser_info(self.request) - """ async def test_transaction_write_schema_txn(self): self.request.match_info = {"tran_id": "dummy"} From a22c0b6c83a670c6d035b9259f3bfc1f6cf2c06a Mon Sep 17 00:00:00 2001 From: Harsh Multani Date: Sat, 8 May 2021 00:55:28 +0530 Subject: [PATCH 3/6] Resolved Merged Conflicts Signed-off-by: Harsh Multani --- .../endorse_transaction/v1_0/routes.py | 40 +------------------ .../v1_0/tests/test_routes.py | 11 +---- 2 files changed, 4 insertions(+), 47 deletions(-) diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py index 39da9932ea..c78d5ae450 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py @@ -1,3 +1,4 @@ + """Endorse Transaction handling admin routes.""" from aiohttp import web @@ -118,13 +119,10 @@ class EndorserInfoSchema(OpenAPISchema): async def transactions_list(request: web.BaseRequest): """ Request handler for searching transaction records. - Args: request: aiohttp request object - Returns: The transaction list response - """ context: AdminRequestContext = request["context"] @@ -150,13 +148,10 @@ async def transactions_list(request: web.BaseRequest): async def transactions_retrieve(request: web.BaseRequest): """ Request handler for fetching a single transaction record. - Args: request: aiohttp request object - Returns: The transaction record response - """ context: AdminRequestContext = request["context"] @@ -186,13 +181,10 @@ async def transactions_retrieve(request: web.BaseRequest): async def transaction_create_request(request: web.BaseRequest): """ Request handler for creating a new transaction record and request. - Args: request: aiohttp request object - Returns: The transaction record - """ context: AdminRequestContext = request["context"] @@ -268,13 +260,10 @@ async def transaction_create_request(request: web.BaseRequest): async def endorse_transaction_response(request: web.BaseRequest): """ Request handler for creating an endorsed transaction response. - Args: request: aiohttp request object - Returns: The updated transaction record details - """ context: AdminRequestContext = request["context"] @@ -372,13 +361,10 @@ async def endorse_transaction_response(request: web.BaseRequest): async def refuse_transaction_response(request: web.BaseRequest): """ Request handler for creating a refused transaction response. - Args: request: aiohttp request object - Returns: The updated transaction record details - """ context: AdminRequestContext = request["context"] @@ -453,13 +439,10 @@ async def refuse_transaction_response(request: web.BaseRequest): async def cancel_transaction(request: web.BaseRequest): """ Request handler for cancelling a Transaction request. - Args: request: aiohttp request object - Returns: The updated transaction record details - """ context: AdminRequestContext = request["context"] @@ -520,13 +503,10 @@ async def cancel_transaction(request: web.BaseRequest): async def transaction_resend(request: web.BaseRequest): """ Request handler for resending a transaction request. - Args: request: aiohttp request object - Returns: The updated transaction record details - """ context: AdminRequestContext = request["context"] @@ -588,13 +568,10 @@ async def transaction_resend(request: web.BaseRequest): async def set_endorser_role(request: web.BaseRequest): """ Request handler for assigning transaction jobs. - Args: request: aiohttp request object - Returns: The assigned transaction jobs - """ context: AdminRequestContext = request["context"] @@ -630,13 +607,10 @@ async def set_endorser_role(request: web.BaseRequest): async def set_endorser_info(request: web.BaseRequest): """ Request handler for assigning endorser information. - Args: request: aiohttp request object - Returns: The assigned endorser information - """ context: AdminRequestContext = request["context"] @@ -695,13 +669,10 @@ async def set_endorser_info(request: web.BaseRequest): async def transaction_write(request: web.BaseRequest): """ Request handler for writing an endorsed transaction to the ledger. - Args: request: aiohttp request object - Returns: The returned ledger response - """ context: AdminRequestContext = request["context"] @@ -725,14 +696,12 @@ async def transaction_write(request: web.BaseRequest): """ ledger_transaction = transaction.messages_attach[0]["data"]["json"] - ledger = context.inject(BaseLedger, required=False) if not ledger: reason = "No ledger available" if not context.settings.get_value("wallet.type"): reason += ": missing wallet-type?" raise web.HTTPForbidden(reason=reason) - async with ledger: try: ledger_response_json = await shield( @@ -740,9 +709,7 @@ async def transaction_write(request: web.BaseRequest): ) except (IndyIssuerError, LedgerError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err - ledger_response = json.loads(ledger_response_json) - # write the wallet non-secrets record # TODO refactor this code (duplicated from ledger.indy.py) if ledger_response["result"]["txn"]["type"] == "101": @@ -762,7 +729,6 @@ async def transaction_write(request: web.BaseRequest): async with ledger: storage = ledger.get_indy_storage() await storage.add_record(record) - elif ledger_response["result"]["txn"]["type"] == "102": # cred def transaction async with ledger: @@ -771,7 +737,6 @@ async def transaction_write(request: web.BaseRequest): schema_response = await shield(ledger.get_schema(schema_seq_no)) except (IndyIssuerError, LedgerError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err - schema_id = schema_response["id"] schema_id_parts = schema_id.split(":") public_did = ledger_response["result"]["txn"]["metadata"]["from"] @@ -792,7 +757,6 @@ async def transaction_write(request: web.BaseRequest): async with ledger: storage = ledger.get_indy_storage() await storage.add_record(record) - else: # TODO unknown ledger transaction type, just ignore for now ... pass @@ -846,4 +810,4 @@ def post_process_routes(app: web.Application): "name": "endorse-transaction", "description": "Endorse a Transaction", } - ) + ) \ No newline at end of file diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py index d1f754c7ad..764bd144d8 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py @@ -1,3 +1,4 @@ + import json from asynctest import mock as async_mock, TestCase as AsyncTestCase @@ -1486,7 +1487,6 @@ async def test_transaction_write_schema_txn(self): ], ) await test_module.transaction_write(self.request) - mock_response.assert_called_once_with({"...": "..."}) """ @@ -1613,10 +1613,8 @@ async def test_transaction_write_no_ledger_x(self): {"data": {"json": json.dumps({"message": "attached"})}} ], ) - with self.assertRaises(test_module.web.HTTPForbidden): await test_module.transaction_write(self.request) - async def test_transaction_write_ledger_txn_submit_x(self): self.request.match_info = {"tran_id": "dummy"} self.ledger.txn_submit = async_mock.CoroutineMock( @@ -1646,10 +1644,8 @@ async def test_transaction_write_ledger_txn_submit_x(self): {"data": {"json": json.dumps({"message": "attached"})}} ], ) - with self.assertRaises(test_module.web.HTTPBadRequest): await test_module.transaction_write(self.request) - async def test_transaction_write_cred_def_txn(self): self.request.match_info = {"tran_id": "dummy"} self.ledger.txn_submit = async_mock.CoroutineMock( @@ -1702,9 +1698,7 @@ async def test_transaction_write_cred_def_txn(self): ], ) await test_module.transaction_write(self.request) - mock_response.assert_called_once_with({"...": "..."}) - async def test_transaction_write_ledger_cred_def_txn_ledger_get_schema_x(self): self.request.match_info = {"tran_id": "dummy"} self.ledger.txn_submit = async_mock.CoroutineMock( @@ -1748,7 +1742,6 @@ async def test_transaction_write_ledger_cred_def_txn_ledger_get_schema_x(self): {"data": {"json": json.dumps({"message": "attached"})}} ], ) - with self.assertRaises(test_module.web.HTTPBadRequest): await test_module.transaction_write(self.request) """ @@ -1801,4 +1794,4 @@ async def test_post_process_routes(self): mock_app = async_mock.MagicMock(_state={"swagger_dict": {"paths": {}}}) test_module.post_process_routes(mock_app) - assert "tags" in mock_app._state["swagger_dict"] + assert "tags" in mock_app._state["swagger_dict"] \ No newline at end of file From 787134029b6386fb1cb3ef5b42c027f4cd9e3996 Mon Sep 17 00:00:00 2001 From: Harsh Multani Date: Sun, 9 May 2021 00:35:01 +0530 Subject: [PATCH 4/6] Fixed Integration tests and coded unit tests for messages and hanler for ack message. Signed-off-by: Harsh Multani --- ...est_transaction_acknowledgement_handler.py | 84 +++++++++++++++++++ .../tests/test_transaction_acknowledgement.py | 78 +++++++++++++++++ .../endorse_transaction/v1_0/routes.py | 13 ++- .../v1_0/tests/test_routes.py | 3 +- demo/features/steps/0586-sign-transaction.py | 2 +- 5 files changed, 175 insertions(+), 5 deletions(-) create mode 100644 aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/tests/test_transaction_acknowledgement_handler.py create mode 100644 aries_cloudagent/protocols/endorse_transaction/v1_0/messages/tests/test_transaction_acknowledgement.py diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/tests/test_transaction_acknowledgement_handler.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/tests/test_transaction_acknowledgement_handler.py new file mode 100644 index 0000000000..738b02259b --- /dev/null +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/tests/test_transaction_acknowledgement_handler.py @@ -0,0 +1,84 @@ +from asynctest import ( + mock as async_mock, + TestCase as AsyncTestCase, +) + +from ......messaging.request_context import RequestContext +from ......messaging.responder import MockResponder +from ......transport.inbound.receipt import MessageReceipt + +from ...handlers import transaction_acknowledgement_handler as test_module +from ...messages.transaction_acknowledgement import TransactionAcknowledgement +from ......connections.models.conn_record import ConnRecord + + +class TestTransactionAcknowledgementHandler(AsyncTestCase): + async def test_called(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + with async_mock.patch.object( + test_module, "TransactionManager", autospec=True + ) as mock_tran_mgr: + mock_tran_mgr.return_value.receive_transaction_acknowledgement = ( + async_mock.CoroutineMock() + ) + request_context.message = TransactionAcknowledgement() + request_context.connection_record = ConnRecord( + connection_id="b5dc1636-a19a-4209-819f-e8f9984d9897" + ) + request_context.connection_ready = True + handler = test_module.TransactionAcknowledgementHandler() + responder = MockResponder() + await handler.handle(request_context, responder) + + mock_tran_mgr.return_value.receive_transaction_acknowledgement.assert_called_once_with( + request_context.message, request_context.connection_record.connection_id + ) + assert not responder.messages + + async def test_called_not_ready(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + request_context.connection_record = async_mock.MagicMock() + + with async_mock.patch.object( + test_module, "TransactionManager", autospec=True + ) as mock_tran_mgr: + mock_tran_mgr.return_value.receive_transaction_acknowledgement = ( + async_mock.CoroutineMock() + ) + request_context.message = TransactionAcknowledgement() + request_context.connection_ready = False + handler = test_module.TransactionAcknowledgementHandler() + responder = MockResponder() + with self.assertRaises(test_module.HandlerException): + await handler.handle(request_context, responder) + + assert not responder.messages + + async def test_called_x(self): + request_context = RequestContext.test_context() + request_context.message_receipt = MessageReceipt() + + with async_mock.patch.object( + test_module, "TransactionManager", autospec=True + ) as mock_tran_mgr: + mock_tran_mgr.return_value.receive_transaction_acknowledgement = ( + async_mock.CoroutineMock( + side_effect=test_module.TransactionManagerError() + ) + ) + request_context.message = TransactionAcknowledgement() + request_context.connection_record = ConnRecord( + connection_id="b5dc1636-a19a-4209-819f-e8f9984d9897" + ) + request_context.connection_ready = True + handler = test_module.TransactionAcknowledgementHandler() + responder = MockResponder() + await handler.handle(request_context, responder) + + mock_tran_mgr.return_value.receive_transaction_acknowledgement.assert_called_once_with( + request_context.message, request_context.connection_record.connection_id + ) + assert not responder.messages diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/tests/test_transaction_acknowledgement.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/tests/test_transaction_acknowledgement.py new file mode 100644 index 0000000000..7be8949de8 --- /dev/null +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/tests/test_transaction_acknowledgement.py @@ -0,0 +1,78 @@ +from unittest import mock, TestCase + +from asynctest import TestCase as AsyncTestCase + +from .....didcomm_prefix import DIDCommPrefix + +from ...message_types import TRANSACTION_ACKNOWLEDGEMENT + +from ..transaction_acknowledgement import TransactionAcknowledgement + + +class TestConfig: + test_thread_id = "3fa85f64-5717-4562-b3fc-2c963f66afa6" + + +class TestTransactionAcknowledgement(TestCase, TestConfig): + def setUp(self): + self.transaction_acknowledgement = TransactionAcknowledgement( + thread_id=self.test_thread_id + ) + + def test_init(self): + """Test initialization.""" + assert self.transaction_acknowledgement.thread_id == self.test_thread_id + + def test_type(self): + """Test type.""" + assert self.transaction_acknowledgement._type == DIDCommPrefix.qualify_current( + TRANSACTION_ACKNOWLEDGEMENT + ) + + @mock.patch( + "aries_cloudagent.protocols.endorse_transaction.v1_0.messages." + "transaction_acknowledgement.TransactionAcknowledgementSchema.load" + ) + def test_deserialize(self, mock_transaction_acknowledgement_schema_load): + """ + Test deserialization. + """ + obj = self.transaction_acknowledgement + + transaction_acknowledgement = TransactionAcknowledgement.deserialize(obj) + mock_transaction_acknowledgement_schema_load.assert_called_once_with(obj) + + assert ( + transaction_acknowledgement + is mock_transaction_acknowledgement_schema_load.return_value + ) + + @mock.patch( + "aries_cloudagent.protocols.endorse_transaction.v1_0.messages." + "transaction_acknowledgement.TransactionAcknowledgementSchema.dump" + ) + def test_serialize(self, mock_transaction_acknowledgement_schema_dump): + """ + Test serialization. + """ + transaction_acknowledgement_dict = self.transaction_acknowledgement.serialize() + mock_transaction_acknowledgement_schema_dump.assert_called_once_with( + self.transaction_acknowledgement + ) + + assert ( + transaction_acknowledgement_dict + is mock_transaction_acknowledgement_schema_dump.return_value + ) + + +class TestTransactionAcknowledgementSchema(AsyncTestCase, TestConfig): + """Test transaction acknowledgement schema.""" + + async def test_make_model(self): + transaction_acknowledgement = TransactionAcknowledgement( + thread_id=self.test_thread_id + ) + data = transaction_acknowledgement.serialize() + model_instance = TransactionAcknowledgement.deserialize(data) + assert type(model_instance) is type(transaction_acknowledgement) diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py index c78d5ae450..64ce6f16c0 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py @@ -1,4 +1,3 @@ - """Endorse Transaction handling admin routes.""" from aiohttp import web @@ -119,6 +118,7 @@ class EndorserInfoSchema(OpenAPISchema): async def transactions_list(request: web.BaseRequest): """ Request handler for searching transaction records. + Args: request: aiohttp request object Returns: @@ -148,6 +148,7 @@ async def transactions_list(request: web.BaseRequest): async def transactions_retrieve(request: web.BaseRequest): """ Request handler for fetching a single transaction record. + Args: request: aiohttp request object Returns: @@ -181,6 +182,7 @@ async def transactions_retrieve(request: web.BaseRequest): async def transaction_create_request(request: web.BaseRequest): """ Request handler for creating a new transaction record and request. + Args: request: aiohttp request object Returns: @@ -260,6 +262,7 @@ async def transaction_create_request(request: web.BaseRequest): async def endorse_transaction_response(request: web.BaseRequest): """ Request handler for creating an endorsed transaction response. + Args: request: aiohttp request object Returns: @@ -361,6 +364,7 @@ async def endorse_transaction_response(request: web.BaseRequest): async def refuse_transaction_response(request: web.BaseRequest): """ Request handler for creating a refused transaction response. + Args: request: aiohttp request object Returns: @@ -439,6 +443,7 @@ async def refuse_transaction_response(request: web.BaseRequest): async def cancel_transaction(request: web.BaseRequest): """ Request handler for cancelling a Transaction request. + Args: request: aiohttp request object Returns: @@ -503,6 +508,7 @@ async def cancel_transaction(request: web.BaseRequest): async def transaction_resend(request: web.BaseRequest): """ Request handler for resending a transaction request. + Args: request: aiohttp request object Returns: @@ -568,6 +574,7 @@ async def transaction_resend(request: web.BaseRequest): async def set_endorser_role(request: web.BaseRequest): """ Request handler for assigning transaction jobs. + Args: request: aiohttp request object Returns: @@ -607,6 +614,7 @@ async def set_endorser_role(request: web.BaseRequest): async def set_endorser_info(request: web.BaseRequest): """ Request handler for assigning endorser information. + Args: request: aiohttp request object Returns: @@ -669,6 +677,7 @@ async def set_endorser_info(request: web.BaseRequest): async def transaction_write(request: web.BaseRequest): """ Request handler for writing an endorsed transaction to the ledger. + Args: request: aiohttp request object Returns: @@ -810,4 +819,4 @@ def post_process_routes(app: web.Application): "name": "endorse-transaction", "description": "Endorse a Transaction", } - ) \ No newline at end of file + ) diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py index 764bd144d8..7948831ed2 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py @@ -1,4 +1,3 @@ - import json from asynctest import mock as async_mock, TestCase as AsyncTestCase @@ -1794,4 +1793,4 @@ async def test_post_process_routes(self): mock_app = async_mock.MagicMock(_state={"swagger_dict": {"paths": {}}}) test_module.post_process_routes(mock_app) - assert "tags" in mock_app._state["swagger_dict"] \ No newline at end of file + assert "tags" in mock_app._state["swagger_dict"] diff --git a/demo/features/steps/0586-sign-transaction.py b/demo/features/steps/0586-sign-transaction.py index ed95bedf28..d1bff9dda1 100644 --- a/demo/features/steps/0586-sign-transaction.py +++ b/demo/features/steps/0586-sign-transaction.py @@ -160,7 +160,7 @@ def step_impl(context, agent_name): agent["agent"], "/transactions/" + txn_id + "/write" ) - assert written_txn["state"] == "transaction_completed" + assert written_txn["state"] == "transaction_acked" @then('"{agent_name}" has written the schema {schema_name} to the ledger') From 978c18acbe9a84de1f88b3520022a97fb85d6e56 Mon Sep 17 00:00:00 2001 From: Harsh Multani Date: Sun, 9 May 2021 17:51:10 +0530 Subject: [PATCH 5/6] Fixed Unit tests for routes and manager (Endorser Protocol) Signed-off-by: Harsh Multani --- .../v1_0/tests/test_manager.py | 57 +++- .../v1_0/tests/test_routes.py | 267 +----------------- 2 files changed, 60 insertions(+), 264 deletions(-) diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py index 66bbc77754..0b7ece6f92 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py @@ -1,4 +1,5 @@ import uuid +import json from aiohttp import web from asynctest import TestCase as AsyncTestCase @@ -16,6 +17,15 @@ from ..transaction_jobs import TransactionJob from ..messages.transaction_request import TransactionRequest +from ..messages.transaction_acknowledgement import TransactionAcknowledgement +from ..transaction_jobs import TransactionJob +from .....ledger.base import BaseLedger + +TEST_DID = "LjgpST2rjsoxYegQDRm7EL" +SCHEMA_NAME = "bc-reg" +SCHEMA_TXN = 12 +SCHEMA_ID = f"{TEST_DID}:2:{SCHEMA_NAME}:1.0" +CRED_DEF_ID = f"{TEST_DID}:3:CL:12:tag1" class TestTransactionManager(AsyncTestCase): @@ -97,6 +107,9 @@ async def setUp(self): self.test_endorser_verkey = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" self.test_refuser_did = "AGDEjaMunDtFtBVrn1qPKQ" + self.ledger = async_mock.create_autospec(BaseLedger) + self.session.context.injector.bind_instance(BaseLedger, self.ledger) + self.manager = TransactionManager(self.session) assert self.manager.session @@ -358,22 +371,50 @@ async def test_receive_endorse_response(self): transaction_record.messages_attach[0]["data"]["json"] == self.test_signature ) - """ async def test_complete_transaction(self): transaction_record = await self.manager.create_record( messages_attach=self.test_messages_attach, connection_id=self.test_connection_id, ) + + self.ledger.get_indy_storage = async_mock.MagicMock( + return_value=async_mock.MagicMock(add_record=async_mock.CoroutineMock()) + ) + self.ledger.txn_submit = async_mock.CoroutineMock( + return_value=json.dumps( + { + "result": { + "txn": {"type": "101", "metadata": {"from": TEST_DID}}, + "txnMetadata": {"txnId": SCHEMA_ID}, + } + } + ) + ) + with async_mock.patch.object( TransactionRecord, "save", autospec=True - ) as save_record: - transaction_record = await self.manager.complete_transaction( - transaction_record + ) as save_record, async_mock.patch.object( + ConnRecord, "retrieve_by_id" + ) as mock_conn_rec_retrieve: + + mock_conn_rec_retrieve.return_value = async_mock.MagicMock( + metadata_get=async_mock.CoroutineMock( + return_value={ + "transaction_their_job": ( + TransactionJob.TRANSACTION_ENDORSER.name + ), + "transaction_my_job": (TransactionJob.TRANSACTION_AUTHOR.name), + } + ) ) + + ( + transaction_record, + transaction_acknowledgement_message, + ) = await self.manager.complete_transaction(transaction_record) save_record.assert_called_once() - assert transaction_record.state == TransactionRecord.STATE_TRANSACTION_COMPLETED - """ + assert transaction_record.state == TransactionRecord.STATE_TRANSACTION_ACKED async def test_create_refuse_response_bad_state(self): transaction_record = await self.manager.create_record( @@ -654,7 +695,6 @@ async def test_set_transaction_their_job(self): for i in range(2): await self.manager.set_transaction_their_job(mock_job, mock_receipt) - """ async def test_set_transaction_their_job_conn_not_found(self): mock_job = async_mock.MagicMock() mock_receipt = async_mock.MagicMock() @@ -664,6 +704,5 @@ async def test_set_transaction_their_job_conn_not_found(self): ) as mock_retrieve: mock_retrieve.side_effect = StorageNotFoundError() - with self.assertRaises(web.HTTPNotFound): + with self.assertRaises(TransactionManagerError): await self.manager.set_transaction_their_job(mock_job, mock_receipt) - """ diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py index 7948831ed2..898e621625 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py @@ -1447,39 +1447,27 @@ async def test_set_endorser_info_my_wrong_job_x(self): with self.assertRaises(test_module.web.HTTPForbidden): await test_module.set_endorser_info(self.request) - """ async def test_transaction_write_schema_txn(self): self.request.match_info = {"tran_id": "dummy"} with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_conn_rec_retrieve, async_mock.patch.object( TransactionRecord, "retrieve_by_id", async_mock.CoroutineMock() ) as mock_txn_rec_retrieve, async_mock.patch.object( test_module, "TransactionManager", async_mock.MagicMock() ) as mock_txn_mgr, async_mock.patch.object( test_module.web, "json_response" ) as mock_response: - mock_txn_mgr.return_value = async_mock.MagicMock( - complete_transaction=async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # txn record - serialize=async_mock.MagicMock(return_value={"...": "..."}) - ) - ) - ) - mock_conn_rec_retrieve.return_value = async_mock.MagicMock( - metadata_get=async_mock.CoroutineMock( - return_value={ - "transaction_my_job": ( - test_module.TransactionJob.TRANSACTION_AUTHOR.name - ), - "transaction_their_job": ( - test_module.TransactionJob.TRANSACTION_ENDORSER.name - ), - } - ) + + mock_txn_mgr.return_value.complete_transaction = async_mock.CoroutineMock() + + mock_txn_mgr.return_value.complete_transaction.return_value = ( + async_mock.CoroutineMock( + serialize=async_mock.MagicMock(return_value={"...": "..."}) + ), + async_mock.CoroutineMock(), ) + mock_txn_rec_retrieve.return_value = async_mock.MagicMock( - serialize=async_mock.MagicMock(return_value={"...": "..."}), + serialize=async_mock.MagicMock(), state=TransactionRecord.STATE_TRANSACTION_ENDORSED, messages_attach=[ {"data": {"json": json.dumps({"message": "attached"})}} @@ -1487,7 +1475,6 @@ async def test_transaction_write_schema_txn(self): ) await test_module.transaction_write(self.request) mock_response.assert_called_once_with({"...": "..."}) - """ async def test_transaction_write_not_found_x(self): self.request.match_info = {"tran_id": "dummy"} @@ -1511,68 +1498,12 @@ async def test_transaction_write_base_model_x(self): with self.assertRaises(test_module.web.HTTPBadRequest): await test_module.transaction_write(self.request) - async def test_transaction_write_no_jobs_x(self): - self.request.match_info = {"tran_id": "dummy"} - - with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_conn_rec_retrieve, async_mock.patch.object( - TransactionRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_txn_rec_retrieve: - mock_conn_rec_retrieve.return_value = async_mock.MagicMock( - metadata_get=async_mock.CoroutineMock(return_value=None) - ) - mock_txn_rec_retrieve.return_value = async_mock.MagicMock( - serialize=async_mock.MagicMock(return_value={"...": "..."}) - ) - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.transaction_write(self.request) - - async def test_transaction_write_my_wrong_job_x(self): - self.request.match_info = {"tran_id": "dummy"} - - with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_conn_rec_retrieve, async_mock.patch.object( - TransactionRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_txn_rec_retrieve: - mock_conn_rec_retrieve.return_value = async_mock.MagicMock( - metadata_get=async_mock.CoroutineMock( - return_value={ - "transaction_their_job": ( - test_module.TransactionJob.TRANSACTION_ENDORSER.name - ), - "transaction_my_job": "a suffusion of yellow", - } - ) - ) - mock_txn_rec_retrieve.return_value = async_mock.MagicMock( - serialize=async_mock.MagicMock(return_value={"...": "..."}) - ) - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.transaction_write(self.request) - async def test_transaction_write_wrong_state_x(self): self.request.match_info = {"tran_id": "dummy"} with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_conn_rec_retrieve, async_mock.patch.object( TransactionRecord, "retrieve_by_id", async_mock.CoroutineMock() ) as mock_txn_rec_retrieve: - mock_conn_rec_retrieve.return_value = async_mock.MagicMock( - metadata_get=async_mock.CoroutineMock( - return_value={ - "transaction_my_job": ( - test_module.TransactionJob.TRANSACTION_AUTHOR.name - ), - "transaction_their_job": ( - test_module.TransactionJob.TRANSACTION_ENDORSER.name - ), - } - ) - ) + mock_txn_rec_retrieve.return_value = async_mock.MagicMock( serialize=async_mock.MagicMock(return_value={"...": "..."}), state=TransactionRecord.STATE_TRANSACTION_CREATED, @@ -1584,172 +1515,9 @@ async def test_transaction_write_wrong_state_x(self): with self.assertRaises(test_module.web.HTTPForbidden): await test_module.transaction_write(self.request) - """ - async def test_transaction_write_no_ledger_x(self): - self.request.match_info = {"tran_id": "dummy"} - self.context.injector.clear_binding(BaseLedger) - with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_conn_rec_retrieve, async_mock.patch.object( - TransactionRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_txn_rec_retrieve: - mock_conn_rec_retrieve.return_value = async_mock.MagicMock( - metadata_get=async_mock.CoroutineMock( - return_value={ - "transaction_my_job": ( - test_module.TransactionJob.TRANSACTION_AUTHOR.name - ), - "transaction_their_job": ( - test_module.TransactionJob.TRANSACTION_ENDORSER.name - ), - } - ) - ) - mock_txn_rec_retrieve.return_value = async_mock.MagicMock( - serialize=async_mock.MagicMock(return_value={"...": "..."}), - state=TransactionRecord.STATE_TRANSACTION_ENDORSED, - messages_attach=[ - {"data": {"json": json.dumps({"message": "attached"})}} - ], - ) - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.transaction_write(self.request) - async def test_transaction_write_ledger_txn_submit_x(self): - self.request.match_info = {"tran_id": "dummy"} - self.ledger.txn_submit = async_mock.CoroutineMock( - side_effect=test_module.LedgerError() - ) - with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_conn_rec_retrieve, async_mock.patch.object( - TransactionRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_txn_rec_retrieve: - mock_conn_rec_retrieve.return_value = async_mock.MagicMock( - metadata_get=async_mock.CoroutineMock( - return_value={ - "transaction_my_job": ( - test_module.TransactionJob.TRANSACTION_AUTHOR.name - ), - "transaction_their_job": ( - test_module.TransactionJob.TRANSACTION_ENDORSER.name - ), - } - ) - ) - mock_txn_rec_retrieve.return_value = async_mock.MagicMock( - serialize=async_mock.MagicMock(return_value={"...": "..."}), - state=TransactionRecord.STATE_TRANSACTION_ENDORSED, - messages_attach=[ - {"data": {"json": json.dumps({"message": "attached"})}} - ], - ) - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.transaction_write(self.request) - async def test_transaction_write_cred_def_txn(self): - self.request.match_info = {"tran_id": "dummy"} - self.ledger.txn_submit = async_mock.CoroutineMock( - return_value=json.dumps( - { - "result": { - "txn": { - "type": "102", - "metadata": {"from": TEST_DID}, - "data": {"ref": 1000}, - }, - "txnMetadata": {"txnId": SCHEMA_ID}, - } - } - ) - ) - with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_conn_rec_retrieve, async_mock.patch.object( - TransactionRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_txn_rec_retrieve, async_mock.patch.object( - test_module, "TransactionManager", async_mock.MagicMock() - ) as mock_txn_mgr, async_mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - mock_txn_mgr.return_value = async_mock.MagicMock( - complete_transaction=async_mock.CoroutineMock( - return_value=async_mock.MagicMock( # txn record - serialize=async_mock.MagicMock(return_value={"...": "..."}) - ) - ) - ) - mock_conn_rec_retrieve.return_value = async_mock.MagicMock( - metadata_get=async_mock.CoroutineMock( - return_value={ - "transaction_my_job": ( - test_module.TransactionJob.TRANSACTION_AUTHOR.name - ), - "transaction_their_job": ( - test_module.TransactionJob.TRANSACTION_ENDORSER.name - ), - } - ) - ) - mock_txn_rec_retrieve.return_value = async_mock.MagicMock( - serialize=async_mock.MagicMock(return_value={"...": "..."}), - state=TransactionRecord.STATE_TRANSACTION_ENDORSED, - messages_attach=[ - {"data": {"json": json.dumps({"message": "attached"})}} - ], - ) - await test_module.transaction_write(self.request) - mock_response.assert_called_once_with({"...": "..."}) - async def test_transaction_write_ledger_cred_def_txn_ledger_get_schema_x(self): - self.request.match_info = {"tran_id": "dummy"} - self.ledger.txn_submit = async_mock.CoroutineMock( - return_value=json.dumps( - { - "result": { - "txn": { - "type": "102", - "metadata": {"from": TEST_DID}, - "data": {"ref": 1000}, - }, - "txnMetadata": {"txnId": SCHEMA_ID}, - } - } - ) - ) - self.ledger.get_schema = async_mock.CoroutineMock( - side_effect=test_module.LedgerError() - ) - with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_conn_rec_retrieve, async_mock.patch.object( - TransactionRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_txn_rec_retrieve: - mock_conn_rec_retrieve.return_value = async_mock.MagicMock( - metadata_get=async_mock.CoroutineMock( - return_value={ - "transaction_my_job": ( - test_module.TransactionJob.TRANSACTION_AUTHOR.name - ), - "transaction_their_job": ( - test_module.TransactionJob.TRANSACTION_ENDORSER.name - ), - } - ) - ) - mock_txn_rec_retrieve.return_value = async_mock.MagicMock( - serialize=async_mock.MagicMock(return_value={"...": "..."}), - state=TransactionRecord.STATE_TRANSACTION_ENDORSED, - messages_attach=[ - {"data": {"json": json.dumps({"message": "attached"})}} - ], - ) - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.transaction_write(self.request) - """ - async def test_transaction_write_schema_txn_complete_x(self): self.request.match_info = {"tran_id": "dummy"} with async_mock.patch.object( - ConnRecord, "retrieve_by_id", async_mock.CoroutineMock() - ) as mock_conn_rec_retrieve, async_mock.patch.object( TransactionRecord, "retrieve_by_id", async_mock.CoroutineMock() ) as mock_txn_rec_retrieve, async_mock.patch.object( test_module, "TransactionManager", async_mock.MagicMock() @@ -1759,18 +1527,7 @@ async def test_transaction_write_schema_txn_complete_x(self): side_effect=test_module.StorageError() ) ) - mock_conn_rec_retrieve.return_value = async_mock.MagicMock( - metadata_get=async_mock.CoroutineMock( - return_value={ - "transaction_my_job": ( - test_module.TransactionJob.TRANSACTION_AUTHOR.name - ), - "transaction_their_job": ( - test_module.TransactionJob.TRANSACTION_ENDORSER.name - ), - } - ) - ) + mock_txn_rec_retrieve.return_value = async_mock.MagicMock( serialize=async_mock.MagicMock(return_value={"...": "..."}), state=TransactionRecord.STATE_TRANSACTION_ENDORSED, From 17c0548dbf38788781e62150aa7d19b2a6b8769a Mon Sep 17 00:00:00 2001 From: Harsh Multani Date: Tue, 11 May 2021 00:41:40 +0530 Subject: [PATCH 6/6] Fixed order of Imports Signed-off-by: Harsh Multani --- ...est_transaction_acknowledgement_handler.py | 2 +- .../endorse_transaction/v1_0/manager.py | 35 +++++++++---------- .../tests/test_transaction_acknowledgement.py | 3 +- .../messages/transaction_acknowledgement.py | 2 +- .../v1_0/models/transaction_record.py | 4 +-- .../endorse_transaction/v1_0/routes.py | 2 +- .../v1_0/tests/test_manager.py | 13 ++++--- .../v1_0/tests/test_routes.py | 6 ++-- demo/features/steps/0586-sign-transaction.py | 4 +-- 9 files changed, 32 insertions(+), 39 deletions(-) diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/tests/test_transaction_acknowledgement_handler.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/tests/test_transaction_acknowledgement_handler.py index 738b02259b..ed4a864727 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/tests/test_transaction_acknowledgement_handler.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/handlers/tests/test_transaction_acknowledgement_handler.py @@ -3,13 +3,13 @@ TestCase as AsyncTestCase, ) +from ......connections.models.conn_record import ConnRecord from ......messaging.request_context import RequestContext from ......messaging.responder import MockResponder from ......transport.inbound.receipt import MessageReceipt from ...handlers import transaction_acknowledgement_handler as test_module from ...messages.transaction_acknowledgement import TransactionAcknowledgement -from ......connections.models.conn_record import ConnRecord class TestTransactionAcknowledgementHandler(AsyncTestCase): diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py index 4aeed97fc9..5d4b9c6df5 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/manager.py @@ -1,35 +1,32 @@ """Class to manage transactions.""" +import json import logging import uuid + from asyncio import shield -import json from time import time -from .models.transaction_record import TransactionRecord -from .messages.transaction_request import TransactionRequest -from .messages.endorsed_transaction_response import EndorsedTransactionResponse -from .messages.refused_transaction_response import RefusedTransactionResponse -from .messages.cancel_transaction import CancelTransaction -from .messages.transaction_resend import TransactionResend -from .messages.transaction_job_to_send import TransactionJobToSend -from .messages.transaction_acknowledgement import TransactionAcknowledgement - from ....connections.models.conn_record import ConnRecord -from ....transport.inbound.receipt import MessageReceipt -from ....storage.error import StorageNotFoundError - from ....core.error import BaseError from ....core.profile import ProfileSession - -from ....ledger.base import BaseLedger - from ....indy.issuer import IndyIssuerError +from ....ledger.base import BaseLedger from ....ledger.error import LedgerError - -from ....storage.base import StorageRecord -from ....messaging.schemas.util import SCHEMA_SENT_RECORD_TYPE from ....messaging.credential_definitions.util import CRED_DEF_SENT_RECORD_TYPE +from ....messaging.schemas.util import SCHEMA_SENT_RECORD_TYPE +from ....storage.base import StorageRecord +from ....storage.error import StorageNotFoundError +from ....transport.inbound.receipt import MessageReceipt + +from .messages.cancel_transaction import CancelTransaction +from .messages.endorsed_transaction_response import EndorsedTransactionResponse +from .messages.refused_transaction_response import RefusedTransactionResponse +from .messages.transaction_acknowledgement import TransactionAcknowledgement +from .messages.transaction_job_to_send import TransactionJobToSend +from .messages.transaction_request import TransactionRequest +from .messages.transaction_resend import TransactionResend +from .models.transaction_record import TransactionRecord from .transaction_jobs import TransactionJob diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/tests/test_transaction_acknowledgement.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/tests/test_transaction_acknowledgement.py index 7be8949de8..09bc6b0a1e 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/tests/test_transaction_acknowledgement.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/tests/test_transaction_acknowledgement.py @@ -1,6 +1,5 @@ -from unittest import mock, TestCase - from asynctest import TestCase as AsyncTestCase +from unittest import mock, TestCase from .....didcomm_prefix import DIDCommPrefix diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/transaction_acknowledgement.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/transaction_acknowledgement.py index ea0e48780e..2765eddb6a 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/transaction_acknowledgement.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/messages/transaction_acknowledgement.py @@ -2,8 +2,8 @@ from marshmallow import EXCLUDE, fields -from .....messaging.valid import UUIDFour from .....messaging.ack.message import Ack, AckSchema +from .....messaging.valid import UUIDFour from ..message_types import TRANSACTION_ACKNOWLEDGEMENT, PROTOCOL_PACKAGE diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/models/transaction_record.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/models/transaction_record.py index 869370901a..2bc980106b 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/models/transaction_record.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/models/transaction_record.py @@ -2,15 +2,13 @@ from marshmallow import fields +from .....core.profile import ProfileSession from .....messaging.models.base_record import ( BaseExchangeRecord, BaseExchangeSchema, ) - from .....messaging.valid import UUIDFour -from .....core.profile import ProfileSession - class TransactionRecord(BaseExchangeRecord): """Represents a single transaction record.""" diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py index 74b7aa004c..be39b084f7 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py @@ -16,9 +16,9 @@ from ....indy.issuer import IndyIssuerError from ....ledger.base import BaseLedger from ....ledger.error import LedgerError +from ....messaging.models.base import BaseModelError from ....messaging.models.openapi import OpenAPISchema from ....messaging.valid import UUIDFour -from ....messaging.models.base import BaseModelError from ....storage.error import StorageError, StorageNotFoundError from ....wallet.base import BaseWallet diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py index 0b7ece6f92..bd6d52fefe 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_manager.py @@ -1,25 +1,24 @@ -import uuid import json +import uuid from aiohttp import web -from asynctest import TestCase as AsyncTestCase from asynctest import mock as async_mock +from asynctest import TestCase as AsyncTestCase from .....cache.base import BaseCache from .....cache.in_memory import InMemoryCache from .....connections.models.conn_record import ConnRecord from .....core.in_memory import InMemoryProfile +from .....ledger.base import BaseLedger from .....storage.error import StorageNotFoundError from ..manager import TransactionManager, TransactionManagerError -from ..models.transaction_record import TransactionRecord from ..messages.messages_attach import MessagesAttach -from ..transaction_jobs import TransactionJob - -from ..messages.transaction_request import TransactionRequest from ..messages.transaction_acknowledgement import TransactionAcknowledgement +from ..messages.transaction_request import TransactionRequest +from ..models.transaction_record import TransactionRecord from ..transaction_jobs import TransactionJob -from .....ledger.base import BaseLedger + TEST_DID = "LjgpST2rjsoxYegQDRm7EL" SCHEMA_NAME = "bc-reg" diff --git a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py index 898e621625..1e478c098b 100644 --- a/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/endorse_transaction/v1_0/tests/test_routes.py @@ -6,13 +6,13 @@ from .....connections.models.conn_record import ConnRecord from .....core.in_memory import InMemoryProfile from .....ledger.base import BaseLedger -from .....wallet.did_method import DIDMethod -from .....wallet.key_type import KeyType from .....wallet.base import BaseWallet from .....wallet.did_info import DIDInfo +from .....wallet.did_method import DIDMethod +from .....wallet.key_type import KeyType -from .. import routes as test_module from ..models.transaction_record import TransactionRecord +from .. import routes as test_module TEST_DID = "LjgpST2rjsoxYegQDRm7EL" diff --git a/demo/features/steps/0586-sign-transaction.py b/demo/features/steps/0586-sign-transaction.py index db1215e7ff..92c2265700 100644 --- a/demo/features/steps/0586-sign-transaction.py +++ b/demo/features/steps/0586-sign-transaction.py @@ -1,6 +1,4 @@ -from behave import given, when, then import json -from time import sleep import time from bdd_support.agent_backchannel_client import ( @@ -11,7 +9,9 @@ async_sleep, read_json_data, ) +from behave import given, when, then from runners.agent_container import AgentContainer +from time import sleep # This step is defined in another feature file