diff --git a/dexa_protocol/v1_0/handlers/pulldata_request_handler.py b/dexa_protocol/v1_0/handlers/pulldata_request_handler.py new file mode 100644 index 0000000..832dfe7 --- /dev/null +++ b/dexa_protocol/v1_0/handlers/pulldata_request_handler.py @@ -0,0 +1,21 @@ +from aries_cloudagent.messaging.base_handler import ( + BaseHandler, + BaseResponder, + RequestContext, +) +from dexa_protocol.v1_0.messages.pulldata_request_message import PullDataRequestMessage +from dexa_sdk.managers.dexa_manager import DexaManager + + +class PullDataRequestMessageHandler(BaseHandler): + async def handle(self, context: RequestContext, responder: BaseResponder): + + assert isinstance(context.message, PullDataRequestMessage) + + # Initialise the manager + mgr = DexaManager(context) + + # Process message. + await mgr.process_pulldata_request_message( + context.message, context.message_receipt + ) diff --git a/dexa_protocol/v1_0/handlers/pulldata_response_handler.py b/dexa_protocol/v1_0/handlers/pulldata_response_handler.py new file mode 100644 index 0000000..d6c1cf6 --- /dev/null +++ b/dexa_protocol/v1_0/handlers/pulldata_response_handler.py @@ -0,0 +1,23 @@ +from aries_cloudagent.messaging.base_handler import ( + BaseHandler, + BaseResponder, + RequestContext, +) +from dexa_protocol.v1_0.messages.pulldata_response_message import ( + PullDataResponseMessage, +) +from dexa_sdk.managers.dexa_manager import DexaManager + + +class PullDataResponseMessageHandler(BaseHandler): + async def handle(self, context: RequestContext, responder: BaseResponder): + + assert isinstance(context.message, PullDataResponseMessage) + + # Initialise the manager + mgr = DexaManager(context) + + # Process message. + await mgr.process_pull_data_response_message( + context.message, context.message_receipt + ) diff --git a/dexa_protocol/v1_0/message_types.py b/dexa_protocol/v1_0/message_types.py index 2f9f132..b5746eb 100644 --- a/dexa_protocol/v1_0/message_types.py +++ b/dexa_protocol/v1_0/message_types.py @@ -19,6 +19,9 @@ NEGOTIATION_RECEIPT = f"dda-negotiation/1.0/receipt" DEACTIVATE_DDA = f"dda/1.0/deactivate" +PULLDATA_REQUEST = f"pull-data/1.0/request" +PULLDATA_RESPONSE = f"pull-data/1.0/response" +PULLDATA_NOTIFICATION = f"pull-data/1.0/notification" PROTOCOL_PACKAGE = "dexa_protocol.v1_0" @@ -34,5 +37,7 @@ ACCEPT_DDA: f"{PROTOCOL_PACKAGE}.messages.negotiation.accept_dda.AcceptDDAMessage", NEGOTIATION_RECEIPT: f"{PROTOCOL_PACKAGE}.messages.negotiation.dda_negotiation_receipt.DDANegotiationReceiptMessage", DEACTIVATE_DDA: f"{PROTOCOL_PACKAGE}.messages.deactivate_dda.DeactivateDDAMessage", + PULLDATA_REQUEST: f"{PROTOCOL_PACKAGE}.messages.pulldata_request_message.PullDataRequestMessage", + PULLDATA_RESPONSE: f"{PROTOCOL_PACKAGE}.messages.pulldata_response_message.PullDataResponseMessage", } ) diff --git a/dexa_protocol/v1_0/messages/pulldata_request_message.py b/dexa_protocol/v1_0/messages/pulldata_request_message.py new file mode 100644 index 0000000..491f3c9 --- /dev/null +++ b/dexa_protocol/v1_0/messages/pulldata_request_message.py @@ -0,0 +1,29 @@ +from aries_cloudagent.messaging.agent_message import AgentMessage, AgentMessageSchema +from dexa_protocol.v1_0.message_types import PROTOCOL_PACKAGE, PULLDATA_REQUEST +from marshmallow import EXCLUDE, fields + +HANDLER_CLASS = f"{PROTOCOL_PACKAGE}.handlers.pulldata_request_handler.PullDataRequestMessageHandler" + + +class PullDataRequestMessage(AgentMessage): + class Meta: + handler_class = HANDLER_CLASS + message_type = PULLDATA_REQUEST + schema_class = "PullDataRequestMessageSchema" + + def __init__(self, *, dda_instance_id: str = None, nonce: str = None, **kwargs): + super().__init__(**kwargs) + + self.dda_instance_id = dda_instance_id + self.nonce = nonce + + +class PullDataRequestMessageSchema(AgentMessageSchema): + class Meta: + model_class = PullDataRequestMessage + + # Unknown fields are excluded. + unknown = EXCLUDE + + dda_instance_id = fields.Str(required=False) + nonce = fields.Str(required=False) diff --git a/dexa_protocol/v1_0/messages/pulldata_response_message.py b/dexa_protocol/v1_0/messages/pulldata_response_message.py new file mode 100644 index 0000000..cfb5c46 --- /dev/null +++ b/dexa_protocol/v1_0/messages/pulldata_response_message.py @@ -0,0 +1,29 @@ +from aries_cloudagent.messaging.agent_message import AgentMessage, AgentMessageSchema +from dexa_protocol.v1_0.message_types import PROTOCOL_PACKAGE, PULLDATA_RESPONSE +from marshmallow import EXCLUDE, fields + +HANDLER_CLASS = f"{PROTOCOL_PACKAGE}.handlers.pulldata_response_handler.PullDataResponseMessageHandler" + + +class PullDataResponseMessage(AgentMessage): + class Meta: + handler_class = HANDLER_CLASS + message_type = PULLDATA_RESPONSE + schema_class = "PullDataResponseMessageSchema" + + def __init__(self, *, ds_eth_address: str = None, nonce: str = None, **kwargs): + super().__init__(**kwargs) + + self.ds_eth_address = ds_eth_address + self.nonce = nonce + + +class PullDataResponseMessageSchema(AgentMessageSchema): + class Meta: + model_class = PullDataResponseMessage + + # Unknown fields are excluded. + unknown = EXCLUDE + + ds_eth_address = fields.Str(required=False) + nonce = fields.Str(required=False) diff --git a/dexa_protocol/v1_0/routes/dda_routes.py b/dexa_protocol/v1_0/routes/dda_routes.py index 46a7818..2cacd84 100644 --- a/dexa_protocol/v1_0/routes/dda_routes.py +++ b/dexa_protocol/v1_0/routes/dda_routes.py @@ -2,21 +2,17 @@ from aiohttp_apispec import docs, match_info_schema, querystring_schema from dexa_protocol.v1_0.routes.maps.tag_maps import TAGS_DDA_LABEL from dexa_protocol.v1_0.routes.openapi.schemas import ( - CreateDDATemplateRequestQueryStringSchema, - DDATemplateMatchInfoSchema, + CreateDDATemplateRequestQueryStringSchema, DDATemplateMatchInfoSchema, DeactivateDDAMatchInfoSchema, ListDDAPublishedInMarketplaceQueryStringSchema, - PublishDDAToMarketplaceMatchInfoSchema, - QueryDDAInstancesQueryStringSchema, - QueryDDATemplateQueryStringSchema, - RequestDDAFromDataSourceMatchInfoSchema, - UpdateDDATemplateQueryStringSchema, -) + PublishDDAToMarketplaceMatchInfoSchema, QueryDDAInstancesQueryStringSchema, + QueryDDATemplateQueryStringSchema, QueryPullDataRecordsQueryStringSchema, + RequestDDAFromDataSourceMatchInfoSchema, SendPullDataRequestMatchInfo, + UpdateDDATemplateQueryStringSchema) from dexa_sdk.managers.dexa_manager import DexaManager from dexa_sdk.utils import clean_and_get_field_from_dict -from mydata_did.v1_0.routes.maps.tag_maps import ( - TAGS_DATA_AGREEMENT_AUDITOR_FUNCTIONS_LABEL, -) +from mydata_did.v1_0.routes.maps.tag_maps import \ + TAGS_DATA_AGREEMENT_AUDITOR_FUNCTIONS_LABEL from mydata_did.v1_0.utils.util import str_to_bool @@ -285,3 +281,60 @@ async def deactivate_dda_instance_handler(request: web.BaseRequest): await mgr.send_deactivate_dda_message(instance_id) return web.json_response({}, status=204) + + +@docs(tags=[TAGS_DDA_LABEL], summary="Send pull data request.") +@match_info_schema(SendPullDataRequestMatchInfo()) +async def send_pulldata_request_handler(request: web.BaseRequest): + """Send pull data request handler + + Args: + request (web.BaseRequest): Request. + """ + # Context + context = request.app["request_context"] + + # Path parameters + instance_id = request.match_info["instance_id"] + + # Initialise manager. + mgr = DexaManager(context) + + # Call the function. + await mgr.send_pulldata_request_message(instance_id) + + return web.json_response({}, status=204) + + +@docs( + tags=[TAGS_DATA_AGREEMENT_AUDITOR_FUNCTIONS_LABEL], + summary="Query pull data records.", +) +@querystring_schema(QueryPullDataRecordsQueryStringSchema()) +async def query_pull_data_records_handler(request: web.BaseRequest): + """ + Request handler for querying pull data records. + """ + + # Context + context = request.app["request_context"] + + dda_instance_id = clean_and_get_field_from_dict(request.query, "dda_instance_id") + dda_template_id = clean_and_get_field_from_dict(request.query, "dda_template_id") + page = clean_and_get_field_from_dict(request.query, "page") + page = int(page) if page is not None else page + page_size = clean_and_get_field_from_dict(request.query, "page_size") + page_size = int(page_size) if page_size is not None else page_size + + # Initialise Dexa manager + manager = DexaManager(context=context) + + # Get the data agreement instances + paginationResult = await manager.query_pull_data_records( + dda_instance_id=dda_instance_id, + dda_template_id=dda_template_id, + page=page if page else 1, + page_size=page_size if page_size else 10, + ) + + return web.json_response(paginationResult._asdict()) diff --git a/dexa_protocol/v1_0/routes/maps/route_maps.py b/dexa_protocol/v1_0/routes/maps/route_maps.py index aee30f7..8c6013f 100644 --- a/dexa_protocol/v1_0/routes/maps/route_maps.py +++ b/dexa_protocol/v1_0/routes/maps/route_maps.py @@ -8,7 +8,9 @@ publish_dda_to_marketplace_handler, query_dda_handler, query_dda_instances_handler, + query_pull_data_records_handler, request_dda_offer_from_ds_handler, + send_pulldata_request_handler, update_dda_template_handler, ) from dexa_protocol.v1_0.routes.marketplace_routes import ( @@ -53,10 +55,19 @@ query_dda_instances_handler, allow_head=False, ), + web.get( + "/v1/auditor/data-disclosure-agreements/pulldata-records", + query_pull_data_records_handler, + allow_head=False, + ), web.post( "/v1/data-disclosure-agreements/instances/{instance_id}/deactivate", deactivate_dda_instance_handler, ), + web.post( + "/v1/data-disclosure-agreements/instances/{instance_id}/pulldata", + send_pulldata_request_handler, + ), ] diff --git a/dexa_protocol/v1_0/routes/openapi/schemas.py b/dexa_protocol/v1_0/routes/openapi/schemas.py index ee1dfed..ed6f503 100644 --- a/dexa_protocol/v1_0/routes/openapi/schemas.py +++ b/dexa_protocol/v1_0/routes/openapi/schemas.py @@ -166,3 +166,20 @@ class DeactivateDDAMatchInfoSchema(OpenAPISchema): """Deactivate DDA match info schema""" instance_id = fields.Str() + + +class SendPullDataRequestMatchInfo(OpenAPISchema): + """Send pull data request match info""" + + instance_id = fields.Str() + + +class QueryPullDataRecordsQueryStringSchema(OpenAPISchema): + """ + Query pull data records query string schema. + """ + + dda_instance_id = fields.Str(required=False) + dda_template_id = fields.Str(required=False) + page = fields.Int(required=False) + page_size = fields.Int(required=False)