Skip to content

Commit 7b73503

Browse files
authoredJul 11, 2023
Merge pull request #2294 from usingtechnology/issue-1652-goal-code
Add Goal and Goal Code to OOB and DIDex Request
2 parents 43952b3 + 030cd39 commit 7b73503

File tree

9 files changed

+193
-2
lines changed

9 files changed

+193
-2
lines changed
 

Diff for: ‎aries_cloudagent/protocols/didexchange/v1_0/manager.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ async def create_request_implicit(
183183
mediation_id: str = None,
184184
use_public_did: bool = False,
185185
alias: str = None,
186+
goal_code: str = None,
187+
goal: str = None,
186188
) -> ConnRecord:
187189
"""
188190
Create and send a request against a public DID only (no explicit invitation).
@@ -193,6 +195,8 @@ async def create_request_implicit(
193195
my_endpoint: my endpoint
194196
mediation_id: record id for mediation with routing_keys, service endpoint
195197
use_public_did: use my public DID for this connection
198+
goal_code: Optional self-attested code for sharing intent of connection
199+
goal: Optional self-attested string for sharing intent of connection
196200
197201
Returns:
198202
The new `ConnRecord` instance
@@ -225,6 +229,8 @@ async def create_request_implicit(
225229
my_label=my_label,
226230
my_endpoint=my_endpoint,
227231
mediation_id=mediation_id,
232+
goal_code=goal_code,
233+
goal=goal,
228234
)
229235
conn_rec.request_id = request._id
230236
conn_rec.state = ConnRecord.State.REQUEST.rfc23
@@ -242,6 +248,8 @@ async def create_request(
242248
my_label: str = None,
243249
my_endpoint: str = None,
244250
mediation_id: str = None,
251+
goal_code: str = None,
252+
goal: str = None,
245253
) -> DIDXRequest:
246254
"""
247255
Create a new connection request for a previously-received invitation.
@@ -252,7 +260,8 @@ async def create_request(
252260
my_endpoint: My endpoint
253261
mediation_id: The record id for mediation that contains routing_keys and
254262
service endpoint
255-
263+
goal_code: Optional self-attested code for sharing intent of connection
264+
goal: Optional self-attested string for sharing intent of connection
256265
Returns:
257266
A new `DIDXRequest` message to send to the other agent
258267
@@ -327,6 +336,8 @@ async def create_request(
327336
label=my_label,
328337
did=conn_rec.my_did,
329338
did_doc_attach=attach,
339+
goal_code=goal_code,
340+
goal=goal,
330341
)
331342
request.assign_thread_id(thid=request._id, pthid=pthid)
332343

Diff for: ‎aries_cloudagent/protocols/didexchange/v1_0/messages/request.py

+23
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ def __init__(
3030
label: str = None,
3131
did: str = None,
3232
did_doc_attach: AttachDecorator = None,
33+
goal_code: str = None,
34+
goal: str = None,
3335
**kwargs,
3436
):
3537
"""
@@ -39,11 +41,20 @@ def __init__(
3941
label: Label for this request
4042
did: DID for this request
4143
did_doc_attach: signed DID doc attachment
44+
goal_code: (optional) is a self-attested code the receiver may want to
45+
display to the user or use in automatically deciding what to do with
46+
the request message. The goal code might be used particularly when the
47+
request is sent to a resolvable DID without reference to a specfic
48+
invitation.
49+
goal: (optional) is a self-attested string that the receiver may want to
50+
display to the user about the context-specific goal of the request message.
4251
"""
4352
super().__init__(**kwargs)
4453
self.label = label
4554
self.did = did
4655
self.did_doc_attach = did_doc_attach
56+
self.goal_code = goal_code
57+
self.goal = goal
4758

4859

4960
class DIDXRequestSchema(AgentMessageSchema):
@@ -67,3 +78,15 @@ class Meta:
6778
description="As signed attachment, DID Doc associated with DID",
6879
data_key="did_doc~attach",
6980
)
81+
goal_code = fields.Str(
82+
required=False,
83+
description="A self-attested code the receiver may want to display to the user "
84+
"or use in automatically deciding what to do with the out-of-band message",
85+
example="issue-vc",
86+
)
87+
goal = fields.Str(
88+
required=False,
89+
description="A self-attested string that the receiver may want to display to the "
90+
"user about the context-specific goal of the out-of-band message",
91+
example="To issue a Faber College Graduate credential",
92+
)

Diff for: ‎aries_cloudagent/protocols/didexchange/v1_0/messages/tests/test_request.py

+8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class TestConfig:
1818
test_verkey = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx"
1919
test_label = "Label"
2020
test_endpoint = "http://localhost"
21+
goal_code = "pytest"
22+
goal = "pass pytest"
2123

2224
def make_did_doc(self):
2325
doc = DIDDoc(did=self.test_did)
@@ -64,12 +66,16 @@ async def setUp(self):
6466
label=TestConfig.test_label,
6567
did=TestConfig.test_did,
6668
did_doc_attach=did_doc_attach,
69+
goal_code=TestConfig.goal_code,
70+
goal=TestConfig.goal,
6771
)
6872

6973
def test_init(self):
7074
"""Test initialization."""
7175
assert self.request.label == TestConfig.test_label
7276
assert self.request.did == TestConfig.test_did
77+
assert self.request.goal_code == TestConfig.goal_code
78+
assert self.request.goal == TestConfig.goal
7379

7480
def test_type(self):
7581
"""Test type."""
@@ -123,6 +129,8 @@ async def setUp(self):
123129
label=TestConfig.test_label,
124130
did=TestConfig.test_did,
125131
did_doc_attach=did_doc_attach,
132+
goal_code="pytest",
133+
goal="pass pytest",
126134
)
127135

128136
async def test_make_model(self):

Diff for: ‎aries_cloudagent/protocols/didexchange/v1_0/routes.py

+16
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ class DIDXCreateRequestImplicitQueryStringSchema(OpenAPISchema):
6262
required=False,
6363
description="Use public DID for this connection",
6464
)
65+
goal_code = fields.Str(
66+
required=False,
67+
description="A self-attested code the receiver may want to display to the user "
68+
"or use in automatically deciding what to do with the out-of-band message",
69+
example="issue-vc",
70+
)
71+
goal = fields.Str(
72+
required=False,
73+
description="A self-attested string that the receiver may want to display to the "
74+
"user about the context-specific goal of the out-of-band message",
75+
example="To issue a Faber College Graduate credential",
76+
)
6577

6678

6779
class DIDXReceiveRequestImplicitQueryStringSchema(OpenAPISchema):
@@ -190,6 +202,8 @@ async def didx_create_request_implicit(request: web.BaseRequest):
190202
mediation_id = request.query.get("mediation_id") or None
191203
alias = request.query.get("alias") or None
192204
use_public_did = json.loads(request.query.get("use_public_did", "null"))
205+
goal_code = request.query.get("goal_code") or None
206+
goal = request.query.get("goal") or None
193207

194208
profile = context.profile
195209
didx_mgr = DIDXManager(profile)
@@ -201,6 +215,8 @@ async def didx_create_request_implicit(request: web.BaseRequest):
201215
mediation_id=mediation_id,
202216
use_public_did=use_public_did,
203217
alias=alias,
218+
goal_code=goal_code,
219+
goal=goal,
204220
)
205221
except StorageNotFoundError as err:
206222
raise web.HTTPNotFound(reason=err.roll_up) from err

Diff for: ‎aries_cloudagent/protocols/out_of_band/v1_0/manager.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ async def create_invitation(
9090
mediation_id: str = None,
9191
service_accept: Optional[Sequence[Text]] = None,
9292
protocol_version: Optional[Text] = None,
93+
goal_code: Optional[Text] = None,
94+
goal: Optional[Text] = None,
9395
) -> InvitationRecord:
9496
"""
9597
Generate new connection invitation.
@@ -111,7 +113,8 @@ async def create_invitation(
111113
service_accept: Optional list of mime types in the order of preference of
112114
the sender that the receiver can use in responding to the message
113115
protocol_version: OOB protocol version [1.0, 1.1]
114-
116+
goal_code: Optional self-attested code for receiver logic
117+
goal: Optional self-attested string for receiver logic
115118
Returns:
116119
Invitation record
117120
@@ -355,6 +358,9 @@ async def create_invitation(
355358
)
356359
]
357360
invi_url = invi_msg.to_url()
361+
if goal and goal_code:
362+
invi_msg.goal_code = goal_code
363+
invi_msg.goal = goal
358364

359365
# Update connection record
360366
if conn_rec:

Diff for: ‎aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py

+22
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ def __init__(
131131
accept: Optional[Sequence[Text]] = None,
132132
version: str = DEFAULT_VERSION,
133133
msg_type: Optional[Text] = None,
134+
goal_code: Optional[Text] = None,
135+
goal: Optional[Text] = None,
134136
**kwargs,
135137
):
136138
"""
@@ -150,6 +152,8 @@ def __init__(
150152
self.requests_attach = list(requests_attach) if requests_attach else []
151153
self.services = services
152154
self.accept = accept
155+
self.goal_code = goal_code
156+
self.goal = goal
153157

154158
@classmethod
155159
def wrap_message(cls, message: dict) -> AttachDecorator:
@@ -264,6 +268,18 @@ class Meta:
264268
"did:sov:WgWxqztrNooG92RXvxSTWv",
265269
],
266270
)
271+
goal_code = fields.Str(
272+
required=False,
273+
description="A self-attested code the receiver may want to display to the user "
274+
"or use in automatically deciding what to do with the out-of-band message",
275+
example="issue-vc",
276+
)
277+
goal = fields.Str(
278+
required=False,
279+
description="A self-attested string that the receiver may want to display to the "
280+
"user about the context-specific goal of the out-of-band message",
281+
example="To issue a Faber College Graduate credential",
282+
)
267283

268284
@validates_schema
269285
def validate_fields(self, data, **kwargs):
@@ -288,6 +304,12 @@ def validate_fields(self, data, **kwargs):
288304
# raise ValidationError(
289305
# "Model must include non-empty services array"
290306
# )
307+
goal = data.get("goal")
308+
goal_code = data.get("goal_code")
309+
if goal and not goal_code:
310+
raise ValidationError("Model cannot have goal without goal_code")
311+
if goal_code and not goal:
312+
raise ValidationError("Model cannot have goal_code without goal")
291313

292314
@post_dump
293315
def post_dump(self, data, **kwargs):

Diff for: ‎aries_cloudagent/protocols/out_of_band/v1_0/messages/tests/test_invitation.py

+85
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,88 @@ def test_assign_msg_type_version_to_model_inst(self):
153153
assert "1.0" in test_req._type
154154
assert "1.2" in test_msg._type
155155
assert "1.1" in InvitationMessage.Meta.message_type
156+
157+
def test_goal_and_code_model(self):
158+
test_msg = InvitationMessage()
159+
assert test_msg.goal is None
160+
assert test_msg.goal_code is None
161+
test_msg = InvitationMessage(goal="pass test", goal_code="pytest.pass")
162+
assert test_msg.goal == "pass test"
163+
assert test_msg.goal_code == "pytest.pass"
164+
165+
def test_mutual_inclusive_goal_and_goal_code(self):
166+
msg = {"aries": "message"}
167+
deco = InvitationMessage.wrap_message(msg)
168+
169+
# populate both goal and goal_code - good
170+
obj_x = {
171+
"label": "label",
172+
"requests~attach": [deco.serialize()],
173+
"goal_code": "pytest.pass",
174+
"goal": "pass test",
175+
}
176+
177+
errs = InvitationMessageSchema().validate(obj_x)
178+
assert errs == {}
179+
180+
# do not include goal and goal_code - good
181+
obj_x = {
182+
"label": "label",
183+
"requests~attach": [deco.serialize()],
184+
}
185+
186+
errs = InvitationMessageSchema().validate(obj_x)
187+
assert errs == {}
188+
189+
# no value for both goal and goal_code - good
190+
obj_x = {
191+
"label": "label",
192+
"requests~attach": [deco.serialize()],
193+
"goal_code": "",
194+
"goal": "",
195+
}
196+
197+
errs = InvitationMessageSchema().validate(obj_x)
198+
assert errs == {}
199+
200+
errs = InvitationMessageSchema().validate(obj_x)
201+
assert errs == {}
202+
203+
# populate only goal - bad
204+
obj_x = {
205+
"label": "label",
206+
"requests~attach": [deco.serialize()],
207+
"goal": "pass test",
208+
}
209+
210+
errs = InvitationMessageSchema().validate(obj_x)
211+
assert errs and len(errs)
212+
assert errs and errs["_schema"]
213+
assert len(errs["_schema"]) == 1
214+
assert "goal" in errs["_schema"][0]
215+
216+
# populate only goal_code - bad
217+
obj_x = {
218+
"label": "label",
219+
"requests~attach": [deco.serialize()],
220+
"goal_code": "pytest.pass",
221+
}
222+
223+
errs = InvitationMessageSchema().validate(obj_x)
224+
assert errs and len(errs)
225+
assert errs and errs["_schema"]
226+
assert len(errs["_schema"]) == 1
227+
assert "goal_code" in errs["_schema"][0]
228+
229+
# goal and goal code cannot be set to null/none
230+
obj_x = {
231+
"label": "label",
232+
"requests~attach": [deco.serialize()],
233+
"goal_code": None,
234+
"goal": None,
235+
}
236+
237+
errs = InvitationMessageSchema().validate(obj_x)
238+
assert len(errs) == 2
239+
assert errs["goal"]
240+
assert errs["goal_code"]

Diff for: ‎aries_cloudagent/protocols/out_of_band/v1_0/routes.py

+16
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,18 @@ class AttachmentDefSchema(OpenAPISchema):
116116
description="Identifier for active mediation record to be used",
117117
**UUID4,
118118
)
119+
goal_code = fields.Str(
120+
required=False,
121+
description="A self-attested code the receiver may want to display to the user "
122+
"or use in automatically deciding what to do with the out-of-band message",
123+
example="issue-vc",
124+
)
125+
goal = fields.Str(
126+
required=False,
127+
description="A self-attested string that the receiver may want to display to the "
128+
"user about the context-specific goal of the out-of-band message",
129+
example="To issue a Faber College Graduate credential",
130+
)
119131

120132

121133
class InvitationReceiveQueryStringSchema(OpenAPISchema):
@@ -172,6 +184,8 @@ async def invitation_create(request: web.BaseRequest):
172184
alias = body.get("alias")
173185
mediation_id = body.get("mediation_id")
174186
protocol_version = body.get("protocol_version")
187+
goal_code = body.get("goal_code")
188+
goal = body.get("goal")
175189

176190
multi_use = json.loads(request.query.get("multi_use", "false"))
177191
auto_accept = json.loads(request.query.get("auto_accept", "null"))
@@ -193,6 +207,8 @@ async def invitation_create(request: web.BaseRequest):
193207
mediation_id=mediation_id,
194208
service_accept=service_accept,
195209
protocol_version=protocol_version,
210+
goal_code=goal_code,
211+
goal=goal,
196212
)
197213
except (StorageNotFoundError, ValidationError, OutOfBandManagerError) as e:
198214
raise web.HTTPBadRequest(reason=e.roll_up)

Diff for: ‎aries_cloudagent/protocols/out_of_band/v1_0/tests/test_routes.py

+4
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ async def test_invitation_create(self):
5959
mediation_id=None,
6060
service_accept=None,
6161
protocol_version=None,
62+
goal_code=None,
63+
goal=None,
6264
)
6365
mock_json_response.assert_called_once_with({"abc": "123"})
6466

@@ -100,6 +102,8 @@ async def test_invitation_create_with_accept(self):
100102
mediation_id=None,
101103
service_accept=["didcomm/aip1", "didcomm/aip2;env=rfc19"],
102104
protocol_version=None,
105+
goal_code=None,
106+
goal=None,
103107
)
104108
mock_json_response.assert_called_once_with({"abc": "123"})
105109

0 commit comments

Comments
 (0)
Please sign in to comment.