Skip to content

Commit

Permalink
Add goal and goal_code to out-of-band invitations
Browse files Browse the repository at this point in the history
Signed-off-by: Jason Sherman <[email protected]>
  • Loading branch information
usingtechnology committed Jul 10, 2023
1 parent 43952b3 commit 6f10dc0
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 1 deletion.
8 changes: 7 additions & 1 deletion aries_cloudagent/protocols/out_of_band/v1_0/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ async def create_invitation(
mediation_id: str = None,
service_accept: Optional[Sequence[Text]] = None,
protocol_version: Optional[Text] = None,
goal_code: Optional[Text] = None,
goal: Optional[Text] = None,
) -> InvitationRecord:
"""
Generate new connection invitation.
Expand All @@ -111,7 +113,8 @@ async def create_invitation(
service_accept: Optional list of mime types in the order of preference of
the sender that the receiver can use in responding to the message
protocol_version: OOB protocol version [1.0, 1.1]
goal_code: Optional self-attested code for receiver logic
goal: Optional self-attested string for receiver logic
Returns:
Invitation record
Expand Down Expand Up @@ -355,6 +358,9 @@ async def create_invitation(
)
]
invi_url = invi_msg.to_url()
if goal and goal_code:
invi_msg.goal_code = goal_code
invi_msg.goal = goal

# Update connection record
if conn_rec:
Expand Down
22 changes: 22 additions & 0 deletions aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ def __init__(
accept: Optional[Sequence[Text]] = None,
version: str = DEFAULT_VERSION,
msg_type: Optional[Text] = None,
goal_code: Optional[Text] = None,
goal: Optional[Text] = None,
**kwargs,
):
"""
Expand All @@ -150,6 +152,8 @@ def __init__(
self.requests_attach = list(requests_attach) if requests_attach else []
self.services = services
self.accept = accept
self.goal_code = goal_code
self.goal = goal

@classmethod
def wrap_message(cls, message: dict) -> AttachDecorator:
Expand Down Expand Up @@ -264,6 +268,18 @@ class Meta:
"did:sov:WgWxqztrNooG92RXvxSTWv",
],
)
goal_code = fields.Str(
required=False,
description="A self-attested code the receiver may want to display to the user "
"or use in automatically deciding what to do with the out-of-band message",
example="issue-vc",
)
goal = fields.Str(
required=False,
description="A self-attested string that the receiver may want to display to the "
"user about the context-specific goal of the out-of-band message",
example="To issue a Faber College Graduate credential",
)

@validates_schema
def validate_fields(self, data, **kwargs):
Expand All @@ -288,6 +304,12 @@ def validate_fields(self, data, **kwargs):
# raise ValidationError(
# "Model must include non-empty services array"
# )
goal = data.get("goal")
goal_code = data.get("goal_code")
if goal and not goal_code:
raise ValidationError("Model cannot have goal without goal_code")
if goal_code and not goal:
raise ValidationError("Model cannot have goal_code without goal")

@post_dump
def post_dump(self, data, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,88 @@ def test_assign_msg_type_version_to_model_inst(self):
assert "1.0" in test_req._type
assert "1.2" in test_msg._type
assert "1.1" in InvitationMessage.Meta.message_type

def test_goal_and_code_model(self):
test_msg = InvitationMessage()
assert test_msg.goal is None
assert test_msg.goal_code is None
test_msg = InvitationMessage(goal="pass test", goal_code="pytest.pass")
assert test_msg.goal == "pass test"
assert test_msg.goal_code == "pytest.pass"

def test_mutual_inclusive_goal_and_goal_code(self):
msg = {"aries": "message"}
deco = InvitationMessage.wrap_message(msg)

# populate both goal and goal_code - good
obj_x = {
"label": "label",
"requests~attach": [deco.serialize()],
"goal_code": "pytest.pass",
"goal": "pass test",
}

errs = InvitationMessageSchema().validate(obj_x)
assert errs == {}

# do not include goal and goal_code - good
obj_x = {
"label": "label",
"requests~attach": [deco.serialize()],
}

errs = InvitationMessageSchema().validate(obj_x)
assert errs == {}

# no value for both goal and goal_code - good
obj_x = {
"label": "label",
"requests~attach": [deco.serialize()],
"goal_code": "",
"goal": "",
}

errs = InvitationMessageSchema().validate(obj_x)
assert errs == {}

errs = InvitationMessageSchema().validate(obj_x)
assert errs == {}

# populate only goal - bad
obj_x = {
"label": "label",
"requests~attach": [deco.serialize()],
"goal": "pass test",
}

errs = InvitationMessageSchema().validate(obj_x)
assert errs and len(errs)
assert errs and errs["_schema"]
assert len(errs["_schema"]) == 1
assert "goal" in errs["_schema"][0]

# populate only goal_code - bad
obj_x = {
"label": "label",
"requests~attach": [deco.serialize()],
"goal_code": "pytest.pass",
}

errs = InvitationMessageSchema().validate(obj_x)
assert errs and len(errs)
assert errs and errs["_schema"]
assert len(errs["_schema"]) == 1
assert "goal_code" in errs["_schema"][0]

# goal and goal code cannot be set to null/none
obj_x = {
"label": "label",
"requests~attach": [deco.serialize()],
"goal_code": None,
"goal": None,
}

errs = InvitationMessageSchema().validate(obj_x)
assert len(errs) == 2
assert errs["goal"]
assert errs["goal_code"]
14 changes: 14 additions & 0 deletions aries_cloudagent/protocols/out_of_band/v1_0/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@ class AttachmentDefSchema(OpenAPISchema):
description="Identifier for active mediation record to be used",
**UUID4,
)
goal_code = fields.Str(
required=False,
description="A self-attested code the receiver may want to display to the user or use in automatically deciding what to do with the out-of-band message",
example="issue-vc",
)
goal = fields.Str(
required=False,
description="A self-attested string that the receiver may want to display to the user about the context-specific goal of the out-of-band message",
example="To issue a Faber College Graduate credential",
)


class InvitationReceiveQueryStringSchema(OpenAPISchema):
Expand Down Expand Up @@ -172,6 +182,8 @@ async def invitation_create(request: web.BaseRequest):
alias = body.get("alias")
mediation_id = body.get("mediation_id")
protocol_version = body.get("protocol_version")
goal_code = body.get("goal_code")
goal = body.get("goal")

multi_use = json.loads(request.query.get("multi_use", "false"))
auto_accept = json.loads(request.query.get("auto_accept", "null"))
Expand All @@ -193,6 +205,8 @@ async def invitation_create(request: web.BaseRequest):
mediation_id=mediation_id,
service_accept=service_accept,
protocol_version=protocol_version,
goal_code=goal_code,
goal=goal,
)
except (StorageNotFoundError, ValidationError, OutOfBandManagerError) as e:
raise web.HTTPBadRequest(reason=e.roll_up)
Expand Down

0 comments on commit 6f10dc0

Please sign in to comment.