Skip to content

Commit 2f214de

Browse files
committed
debug: Add API call to for testing
Signed-off-by: Colton Wolkins (Laptop) <[email protected]>
1 parent 1cec9fe commit 2f214de

File tree

5 files changed

+299
-0
lines changed

5 files changed

+299
-0
lines changed

acapy_agent/protocols_v2/nametag/__init__.py

Whitespace-only changes.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""Version definitions for this protocol."""
2+
3+
versions = [
4+
{
5+
"major_version": 1,
6+
"minimum_minor_version": 0,
7+
"current_minor_version": 0,
8+
"path": "v1_0",
9+
}
10+
]

acapy_agent/protocols_v2/nametag/v1_0/__init__.py

Whitespace-only changes.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""Message type identifiers for Trust Pings."""
2+
3+
import logging
4+
from ....messaging.v2_agent_message import V2AgentMessage
5+
6+
SPEC_URI = "https://didcomm.org/basicmessage/2.0/message"
7+
8+
# Message types
9+
BASIC_MESSAGE = "https://colton.wolkins.net/dev/name-tag/2.0/get-name"
10+
11+
PROTOCOL_PACKAGE = "acapy_agent.protocols_v2.nametag.v1_0"
12+
13+
14+
class basic_message:
15+
"""Basic Message 2.0 DIDComm V2 Protocol."""
16+
17+
async def __call__(self, *args, **kwargs):
18+
"""Call the Handler."""
19+
await self.handle(*args, **kwargs)
20+
21+
@staticmethod
22+
async def handle(context, responder, payload):
23+
"""Handle the incoming message."""
24+
logging.getLogger(__name__)
25+
their_did = context.message_receipt.sender_verkey.split("#")[0]
26+
our_did = context.message_receipt.recipient_verkey.split("#")[0]
27+
error_result = V2AgentMessage(
28+
message={
29+
"type": BASIC_MESSAGE,
30+
"body": {
31+
"content": "Hello from acapy",
32+
},
33+
"to": [their_did],
34+
"from": our_did,
35+
"lang": "en",
36+
}
37+
)
38+
await responder.send_reply(error_result)
39+
40+
41+
HANDLERS = {
42+
BASIC_MESSAGE: f"{PROTOCOL_PACKAGE}.message_types.basic_message",
43+
}.items()
44+
45+
MESSAGE_TYPES = {
46+
BASIC_MESSAGE: f"{PROTOCOL_PACKAGE}.message_types.basic_message",
47+
}
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
"""Trust ping admin routes."""
2+
3+
from aiohttp import web
4+
from aiohttp_apispec import docs, request_schema, response_schema
5+
from marshmallow import fields
6+
from didcomm_messaging import DIDCommMessaging, RoutingService
7+
from didcomm_messaging.resolver import DIDResolver as DMPResolver
8+
9+
from ....admin.decorators.auth import tenant_authentication
10+
from ....admin.request_context import AdminRequestContext
11+
from ....messaging.models.openapi import OpenAPISchema
12+
from ....messaging.valid import UUID4_EXAMPLE
13+
from .message_types import SPEC_URI
14+
15+
from ....wallet.base import BaseWallet
16+
from ....wallet.did_info import DIDInfo
17+
from ....wallet.did_method import (
18+
DIDMethod,
19+
DIDMethods,
20+
)
21+
from ....wallet.did_posture import DIDPosture
22+
from ....messaging.v2_agent_message import V2AgentMessage
23+
from ....connections.models.connection_target import ConnectionTarget
24+
25+
26+
class BaseDIDCommV2Schema(OpenAPISchema):
27+
"""Request schema for performing a ping."""
28+
29+
to_did = fields.Str(
30+
required=True,
31+
allow_none=False,
32+
metadata={"description": "Comment for the ping message"},
33+
)
34+
35+
36+
class PingRequestSchema(BaseDIDCommV2Schema):
37+
"""Request schema for performing a ping."""
38+
39+
response_requested = fields.Bool(
40+
required=False,
41+
allow_none=True,
42+
metadata={"description": "Comment for the ping message"},
43+
)
44+
45+
46+
class PingRequestResponseSchema(OpenAPISchema):
47+
"""Request schema for performing a ping."""
48+
49+
thread_id = fields.Str(
50+
required=False, metadata={"description": "Thread ID of the ping message"}
51+
)
52+
53+
54+
class PingConnIdMatchInfoSchema(OpenAPISchema):
55+
"""Path parameters and validators for request taking connection id."""
56+
57+
conn_id = fields.Str(
58+
required=True,
59+
metadata={"description": "Connection identifier", "example": UUID4_EXAMPLE},
60+
)
61+
62+
63+
def format_did_info(info: DIDInfo):
64+
"""Serialize a DIDInfo object."""
65+
if info:
66+
return {
67+
"did": info.did,
68+
"verkey": info.verkey,
69+
"posture": DIDPosture.get(info.metadata).moniker,
70+
"key_type": info.key_type.key_type,
71+
"method": info.method.method_name,
72+
"metadata": info.metadata,
73+
}
74+
75+
76+
async def get_mydid(request: web.BaseRequest):
77+
"""Get a DID that can be used for communication."""
78+
context: AdminRequestContext = request["context"]
79+
# filter_did = request.query.get("did")
80+
# filter_verkey = request.query.get("verkey")
81+
filter_posture = DIDPosture.get(request.query.get("posture"))
82+
results = []
83+
async with context.session() as session:
84+
did_methods: DIDMethods = session.inject(DIDMethods)
85+
filter_method: DIDMethod | None = did_methods.from_method(
86+
request.query.get("method") or "did:peer:2"
87+
)
88+
# key_types = session.inject(KeyTypes)
89+
# filter_key_type = key_types.from_key_type(request.query.get("key_type", ""))
90+
wallet: BaseWallet | None = session.inject_or(BaseWallet)
91+
if not wallet:
92+
raise web.HTTPForbidden(reason="No wallet available")
93+
else:
94+
dids = await wallet.get_local_dids()
95+
results = [
96+
format_did_info(info)
97+
for info in dids
98+
if (
99+
filter_posture is None
100+
or DIDPosture.get(info.metadata) is DIDPosture.WALLET_ONLY
101+
)
102+
and (not filter_method or info.method == filter_method)
103+
# and (not filter_key_type or info.key_type == filter_key_type)
104+
]
105+
106+
results.sort(key=lambda info: (DIDPosture.get(info["posture"]).ordinal, info["did"]))
107+
our_did = results[0]["did"]
108+
return our_did
109+
110+
111+
async def get_target(request: web.BaseRequest, to_did: str, from_did: str):
112+
"""Get Connection Target from did."""
113+
context: AdminRequestContext = request["context"]
114+
115+
try:
116+
async with context.profile.session() as session:
117+
resolver = session.inject(DMPResolver)
118+
await resolver.resolve(to_did)
119+
except Exception as err:
120+
raise web.HTTPNotFound(reason=str(err)) from err
121+
122+
async with context.session() as session:
123+
ctx = session
124+
messaging = ctx.inject(DIDCommMessaging)
125+
routing_service = ctx.inject(RoutingService)
126+
frm = to_did
127+
services = await routing_service._resolve_services(messaging.resolver, frm)
128+
chain = [
129+
{
130+
"did": frm,
131+
"service": services,
132+
}
133+
]
134+
135+
# Loop through service DIDs until we run out of DIDs to forward to
136+
to_target = services[0].service_endpoint.uri
137+
found_forwardable_service = await routing_service.is_forwardable_service(
138+
messaging.resolver, services[0]
139+
)
140+
while found_forwardable_service:
141+
services = await routing_service._resolve_services(
142+
messaging.resolver, to_target
143+
)
144+
if services:
145+
chain.append(
146+
{
147+
"did": to_target,
148+
"service": services,
149+
}
150+
)
151+
to_target = services[0].service_endpoint.uri
152+
found_forwardable_service = (
153+
await routing_service.is_forwardable_service(
154+
messaging.resolver, services[0]
155+
)
156+
if services
157+
else False
158+
)
159+
reply_destination = [
160+
ConnectionTarget(
161+
did=f"{to_did}#key-1",
162+
endpoint=service.service_endpoint.uri,
163+
recipient_keys=[f"{to_did}#key-1"],
164+
sender_key=from_did + "#key-1",
165+
)
166+
for service in chain[-1]["service"]
167+
]
168+
return reply_destination
169+
170+
171+
class BasicMessageSchema(BaseDIDCommV2Schema):
172+
"""Request schema for performing a ping."""
173+
174+
content = fields.Str(
175+
required=True,
176+
allow_none=False,
177+
metadata={"description": "Basic Message message content"},
178+
)
179+
180+
181+
@docs(tags=["basicmessagev2", "didcommv2"], summary="Send a Basic Message")
182+
@request_schema(BasicMessageSchema())
183+
@response_schema(PingRequestResponseSchema(), 200, description="")
184+
@tenant_authentication
185+
async def basic_message_send(request: web.BaseRequest):
186+
"""Request handler for sending a trust ping to a connection.
187+
188+
Args:
189+
request: aiohttp request object
190+
191+
"""
192+
request["context"]
193+
outbound_handler = request["outbound_message_router"]
194+
body = await request.json()
195+
to_did = body.get("to_did")
196+
message = body.get("content")
197+
198+
our_did = await get_mydid(request)
199+
their_did = to_did
200+
reply_destination = await get_target(request, to_did, our_did)
201+
msg = V2AgentMessage(
202+
message={
203+
"type": "https://colton.wolkins.net/dev/name-tag/2.0/set-name",
204+
"body": {"name": message},
205+
"lang": "en",
206+
"to": [their_did],
207+
"from": our_did,
208+
}
209+
)
210+
await outbound_handler(msg, target_list=reply_destination)
211+
return web.json_response(msg.message)
212+
213+
214+
async def register(app: web.Application):
215+
"""Register routes."""
216+
217+
app.add_routes([web.post("/name-tag/set-name", basic_message_send)])
218+
219+
220+
def post_process_routes(app: web.Application):
221+
"""Amend swagger API."""
222+
223+
# Add top-level tags description
224+
if "tags" not in app._state["swagger_dict"]:
225+
app._state["swagger_dict"]["tags"] = []
226+
app._state["swagger_dict"]["tags"].append(
227+
{
228+
"name": "basicmessagev2",
229+
"description": "Basic Message to contact",
230+
"externalDocs": {"description": "Specification", "url": SPEC_URI},
231+
}
232+
)
233+
app._state["swagger_dict"]["tags"].append(
234+
{
235+
"name": "didcommv2",
236+
"description": "DIDComm V2 based protocols for Interop-a-thon",
237+
"externalDocs": {
238+
"description": "Specification",
239+
"url": "https://didcomm.org",
240+
},
241+
}
242+
)

0 commit comments

Comments
 (0)