Skip to content

Commit a113f76

Browse files
authored
Merge pull request #32 from didx-xyz/YOMA-257/extra_admin_APIs
Complete coverage of ACA-Py Swagger API
2 parents 77dea5a + dfadc0f commit a113f76

File tree

7 files changed

+326
-5
lines changed

7 files changed

+326
-5
lines changed

aries_cloudcontroller/aries_controller_base.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from .controllers.oob import OOBController
2020
from .controllers.action_menu import ActionMenuController
2121
from .controllers.revocation import RevocationController
22+
from .controllers.didexchange import DidExchangeController
2223

2324
import logging
2425

@@ -82,6 +83,8 @@ def __post_init__(self):
8283

8384
self.definitions = DefinitionsController(self.admin_url, self.client_session)
8485

86+
self.didexchange = DidExchangeController(self.admin_url, self.client_session)
87+
8588
self.issuer = IssuerController(
8689
self.admin_url,
8790
self.client_session,
@@ -155,7 +158,7 @@ def add_listener(self, listener):
155158
"topic":"topicname" key-value pairs
156159
"""
157160
try:
158-
pub_topic_path = listener['topic']
161+
pub_topic_path = listener["topic"]
159162
logger.INFO("Subscribing too: " + pub_topic_path)
160163
pub.subscribe(listener["handler"], pub_topic_path)
161164
logger.debug("Lister added for topic : ", pub_topic_path)

aries_cloudcontroller/controllers/connections.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ async def accept_request(self, connection_id: str, my_endpoint: str = None):
160160
# TODO create proper error classes
161161
raise Exception("The connection is not in the request state")
162162

163+
async def get_remote_endpoints(self, connection_id: str):
164+
response = await self.admin_GET(f"/connections/{connection_id}/endpoints")
165+
return response
166+
163167
async def establish_inbound(self, connection_id: str, router_conn_id: str):
164168
response = await self.admin_POST(
165169
f"/connections/{connection_id}/establish-inbound/{router_conn_id}"
@@ -231,3 +235,31 @@ async def is_active(self, connection_id):
231235
logger.error(f"Connection {connection_id} not active")
232236
raise Exception("Connection must be active to send a credential")
233237
return
238+
239+
async def get_connection_metadata(self, connection_id: str, key: str = None):
240+
if key:
241+
response = await self.admin_GET(
242+
f"/connections/{connection_id}/metadata?key={key}"
243+
)
244+
else:
245+
response = await self.admin_GET(f"/connections/{connection_id}/metadata")
246+
return response
247+
248+
async def set_connection_metadata(self, connection_id: str, metadata: dict = None):
249+
if metadata:
250+
metadata = {"metadata": metadata}
251+
response = await self.admin_POST(
252+
f"/connections/{connection_id}/metadata", data=metadata
253+
)
254+
else:
255+
response = await self.admin_POST(f"/connections/{connection_id}/metadata")
256+
return response
257+
258+
async def start_introduction(
259+
self, connection_id: str, target_connection_id: str, message: str = None
260+
):
261+
route = f"/connections/{connection_id}/start-introduction?target_connection_id={target_connection_id}"
262+
if message:
263+
route += f"&message={message}"
264+
response = self.admin_POST(route)
265+
return response
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from .base import BaseController
2+
from aiohttp import ClientSession
3+
import logging
4+
5+
logger = logging.getLogger("aries_controller.didexchange")
6+
7+
8+
class DidExchangeController(BaseController):
9+
def __init__(self, admin_url: str, client_session: ClientSession):
10+
super().__init__(admin_url, client_session)
11+
12+
async def create_request_against_invite(
13+
self,
14+
their_pub_id: str,
15+
mediation_id: str = None,
16+
my_endpoint: str = None,
17+
my_label: str = None,
18+
):
19+
params = {}
20+
if mediation_id:
21+
params["mediation_id"] = mediation_id
22+
if my_endpoint:
23+
params["my_endpoint"] = my_endpoint
24+
if my_label:
25+
params["my_label"] = my_label
26+
response = self.admin_POST(f"/didexchange/create-request", params=params)
27+
return response
28+
29+
async def receive_request_against_invite(
30+
self,
31+
body: {} = None,
32+
alias: str = None,
33+
auto_accept: bool = None,
34+
mediation_id: str = None,
35+
my_endpoint: str = None,
36+
):
37+
params = {}
38+
if alias:
39+
params["alias"] = alias
40+
if auto_accept:
41+
params["auto_accept"] = auto_accept
42+
if mediation_id:
43+
params["mediation_id"] = mediation_id
44+
if my_endpoint:
45+
params["my_endpoint"] = my_endpoint
46+
if body:
47+
response = self.admin_POST(
48+
f"/didexchange/receive-request", body=body, params=params
49+
)
50+
else:
51+
response = self.admin_POST(f"/didexchange/receive-request", params=params)
52+
return response
53+
54+
async def accept_invitation(
55+
self, connection_id: str, my_endpoint: str = None, my_label: str = None
56+
):
57+
params = {}
58+
if my_endpoint:
59+
params["my_endpoint"] = my_endpoint
60+
if my_label:
61+
params["my_label"] = my_label
62+
response = self.admin_POST(
63+
f"/didexchange/{connection_id}/accept-invitation", params=params
64+
)
65+
return response
66+
67+
async def accept_stored_invite(
68+
self, connection_id: str, mediation_id: str = None, my_endpoint: str = None
69+
):
70+
params = {}
71+
if mediation_id:
72+
params["mediation_id"] = mediation_id
73+
if my_endpoint:
74+
params["my_endpoint"] = my_endpoint
75+
response = self.admin_POST(
76+
f"/didexchange/{connection_id}/accept-request", params=params
77+
)
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
from .base import BaseController
2+
from aiohttp import ClientSession
3+
import logging
4+
from ..helpers.utils import extract_did, get_schema_details
5+
6+
logger = logging.getLogger("aries_controller.issuer2")
7+
8+
CRED_PREVIEW = (
9+
"did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/2.0/credential-preview"
10+
)
11+
12+
13+
class IssuerV2Controller(BaseController):
14+
def __init__(
15+
self,
16+
admin_url: str,
17+
client_session: ClientSession,
18+
connection_controller,
19+
wallet_controller,
20+
definition_controller,
21+
):
22+
super().__init__(admin_url, client_session)
23+
self.base_url = "/issue-credential-2.0"
24+
self.connections = connection_controller
25+
self.wallet = wallet_controller
26+
self.definitions = definition_controller
27+
28+
# Fetch all credential exchange records
29+
async def get_records(
30+
self,
31+
connection_id: str = None,
32+
role: str = None,
33+
state: str = None,
34+
thread_id: str = None,
35+
):
36+
params = {}
37+
if connection_id:
38+
params["connection_id"] = connection_id
39+
if role in ["issuer", "holder"]:
40+
params["role"] = role
41+
if state in [
42+
"proposal-sent",
43+
"proposal-received",
44+
"offer-sent",
45+
"offer-received",
46+
"request-sent",
47+
"request-received",
48+
"credential-issued",
49+
"credential-received",
50+
"done",
51+
]:
52+
params["state"] = state
53+
if thread_id:
54+
params["thread_id"] = thread_id
55+
return await self.admin_GET(f"{self.base_url}/records", params=params)
56+
57+
async def get_record_by_id(self, cred_ex_id):
58+
return await self.admin_GET(f"{self.base_url}/records/{cred_ex_id}")
59+
60+
async def create_credential(self, body):
61+
return await self.admin_POST(f"{self.base_url}/create", json_data=body)
62+
63+
# Send holder a credential, automating the entire flow
64+
async def send_credential(
65+
self,
66+
connection_id,
67+
schema_id,
68+
cred_def_id,
69+
attributes,
70+
comment: str = "",
71+
auto_remove: bool = True,
72+
trace: bool = False,
73+
):
74+
75+
body = await self.create_credential_body(
76+
connection_id,
77+
schema_id,
78+
cred_def_id,
79+
attributes,
80+
comment,
81+
auto_remove,
82+
trace,
83+
)
84+
return await self.admin_POST(f"{self.base_url}/send", json_data=body)
85+
86+
# Send Issuer a credential proposal
87+
async def send_proposal(
88+
self,
89+
connection_id,
90+
schema_id,
91+
cred_def_id,
92+
attributes,
93+
comment: str = "",
94+
auto_remove: bool = True,
95+
trace: bool = False,
96+
):
97+
98+
body = await self.create_credential_body(
99+
connection_id,
100+
schema_id,
101+
cred_def_id,
102+
attributes,
103+
comment,
104+
auto_remove,
105+
trace,
106+
)
107+
return await self.admin_POST(f"{self.base_url}/send-proposal", json_data=body)
108+
109+
async def send_offer(
110+
self,
111+
connection_id,
112+
cred_def_id,
113+
attributes,
114+
comment: str = "",
115+
auto_issue: bool = True,
116+
auto_remove: bool = True,
117+
trace: bool = False,
118+
):
119+
await self.connections.is_active(connection_id)
120+
offer_body = {
121+
"cred_def_id": cred_def_id,
122+
"auto_remove": auto_remove,
123+
"trace": trace,
124+
"comment": comment,
125+
"auto_issue": auto_issue,
126+
"credential_preview": {"@type": CRED_PREVIEW, "attributes": attributes},
127+
"connection_id": connection_id,
128+
}
129+
return await self.admin_POST(
130+
f"{self.base_url}/send-offer", json_data=offer_body
131+
)
132+
133+
# Send holder a credential offer in reference to a proposal with preview
134+
async def send_offer_for_record(self, cred_ex_id):
135+
return await self.admin_POST(f"{self.base_url}/records/{cred_ex_id}/send-offer")
136+
137+
# Send issuer a credential request
138+
async def send_request_for_record(self, cred_ex_id):
139+
return await self.admin_POST(
140+
f"{self.base_url}/records/{cred_ex_id}/send-request"
141+
)
142+
143+
# Send holder a credential
144+
async def issue_credential(self, cred_ex_id: str, comment: str = None):
145+
body = {}
146+
if comment:
147+
body = {
148+
"comment": comment,
149+
}
150+
return await self.admin_POST(
151+
f"{self.base_url}/records/{cred_ex_id}/issue", json_data=body
152+
)
153+
154+
# Store a received credential
155+
async def store_credential(self, cred_ex_id: str, credential_id: str = None):
156+
body = {}
157+
if credential_id:
158+
body["credential_id"] = credential_id
159+
return await self.admin_POST(
160+
f"{self.base_url}/records/{cred_ex_id}/store", json_data=body
161+
)
162+
163+
# Remove an existing credential exchange record
164+
async def remove_record(self, cred_ex_id):
165+
return await self.admin_DELETE(f"{self.base_url}/records/{cred_ex_id}")
166+
167+
# Send a problem report for a credential exchange
168+
async def problem_report(self, cred_ex_id, explanation: str):
169+
body = {"explain_ltxt": explanation}
170+
171+
return await self.admin_POST(
172+
f"{self.base_url}/records/{cred_ex_id}/problem-report", json_data=body
173+
)
174+
175+
# Used for both send and send-proposal bodies
176+
async def create_credential_body(
177+
self,
178+
connection_id,
179+
schema_id,
180+
cred_def_id,
181+
attributes,
182+
comment: str = "",
183+
auto_remove: bool = True,
184+
trace: bool = False,
185+
):
186+
# raises error if connection not active
187+
await self.connections.is_active(connection_id)
188+
189+
schema_details = get_schema_details(schema_id)
190+
191+
issuer_did = extract_did(cred_def_id)
192+
193+
body = {
194+
"issuer_did": issuer_did,
195+
"auto_remove": auto_remove,
196+
"credential_proposal": {"@type": CRED_PREVIEW, "attributes": attributes},
197+
"connection_id": connection_id,
198+
"trace": trace,
199+
"comment": comment,
200+
"cred_def_id": cred_def_id,
201+
}
202+
203+
credential_body = {**body, **schema_details}
204+
return credential_body

aries_cloudcontroller/controllers/ledger.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,5 @@ async def get_taa(self):
3737
async def accept_taa(self, data):
3838
return await self.admin_POST(f"{self.base_url}/taa/accept", json_data=data)
3939

40-
# TODO PATCH rotate key pair
40+
async def rotate_pub_key_pair(self):
41+
return await self.admin_PATCH(f"{self.base_url}/rotate-public-did-keypair")

aries_cloudcontroller/controllers/server.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,5 @@ async def get_features(self, query: str = None):
3131

3232
return await self.admin_GET("/features", params=params)
3333

34-
## TODO implement shutdown. Should this be a POST?
34+
async def shutdown(self):
35+
return await self.admin_GET("/shutdown")

aries_cloudcontroller/controllers/wallet.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ async def get_dids(self):
1616
async def create_did(self):
1717
return await self.admin_POST(f"{self.base_url}/did/create")
1818

19+
async def rotate_pub_key_pair(self, did: str):
20+
return await self.admin_PATCH(
21+
f"{self.base_url}/did/local/rotate-keypair?did={did}"
22+
)
23+
1924
async def get_public_did(self):
2025
return await self.admin_GET(f"{self.base_url}/did/public")
2126

@@ -32,5 +37,3 @@ async def set_did_endpoint(self, did, endpoint, endpoint_type):
3237
return await self.admin_POST(
3338
f"{self.base_url}/set-did-endpoint", json_data=body
3439
)
35-
36-
## TODO Patch rotate-keypair

0 commit comments

Comments
 (0)