Skip to content

Commit

Permalink
Merge pull request sicpa-dlab#90 from sicpa-dlab/feat/message-id-auto…
Browse files Browse the repository at this point in the history
…creation

Improve `id` and `thid` management
  • Loading branch information
yvgny committed Feb 28, 2023
2 parents 817a471 + b3f53fd commit ac4696c
Show file tree
Hide file tree
Showing 10 changed files with 56 additions and 40 deletions.
16 changes: 14 additions & 2 deletions didcomm/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
JSON,
DIDCommMessageTypes,
)
from didcomm.core.converters import converter__id
from didcomm.core.converters import converter__id, converter__didcomm_id
from didcomm.core.serialization import json_str_to_dict, json_bytes_to_dict
from didcomm.core.utils import dataclass_to_dict, attrs_to_dict, is_did
from didcomm.core.validators import validator__instance_of
Expand All @@ -33,15 +33,22 @@
class GenericMessage(Generic[T]):
"""
Message consisting of headers and application/protocol specific data (body).
If no `id` is specified, a UUID will be generated.
If no `thid` is specified, it defaults to the `id` value.
In order to convert a message to a DIDComm message for further transporting, call one of the following:
- `pack_encrypted` to build an Encrypted DIDComm message
- `pack_signed` to build a signed DIDComm message
- `pack_plaintext` to build a Plaintext DIDComm message
"""

id: str
type: str
body: T
# if not specified would be auto-generated
id: Optional[Union[str, Callable]] = attr.ib(
converter=converter__didcomm_id,
validator=validator__instance_of(str),
default=None,
)
frm: Optional[DID] = None
to: Optional[List[DID]] = None
created_time: Optional[int] = None
Expand Down Expand Up @@ -71,6 +78,11 @@ class GenericMessage(Generic[T]):
"attachments",
}

def __attrs_post_init__(self):
# If not present, the thid defaults to id (see https://identity.foundation/didcomm-messaging/spec/#threads-2)
if self.thid is None:
self.thid = self.id

def _body_as_dict(self):
if dataclasses.is_dataclass(self.body):
return dataclass_to_dict(self.body)
Expand Down
9 changes: 1 addition & 8 deletions didcomm/protocols/routing/forward.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import logging
import attr
from dataclasses import dataclass
from typing import List, Union, Optional, Callable, Dict
from typing import List, Union, Optional, Dict
from packaging.specifiers import SpecifierSet
from enum import Enum

Expand All @@ -27,7 +27,6 @@
from didcomm.message import GenericMessage, Header, Attachment, AttachmentDataJson
from didcomm.core.types import EncryptResult, DIDCommGeneratorType, DIDCOMM_ORG_DOMAIN
from didcomm.core.defaults import DEF_ENC_ALG_ANON
from didcomm.core.converters import converter__didcomm_id
from didcomm.core.validators import (
validator__instance_of,
validator__didcomm_protocol_mturi,
Expand Down Expand Up @@ -70,12 +69,6 @@ class ForwardBody:

@attr.s(auto_attribs=True)
class ForwardMessage(GenericMessage[ForwardBody]):
# if not specified would be auto-generated
id: Optional[Union[str, Callable]] = attr.ib(
converter=converter__didcomm_id,
validator=validator__instance_of(str),
default=None,
)
type: Optional[str] = attr.ib(
validator=[
validator__instance_of(str),
Expand Down
1 change: 1 addition & 0 deletions tests/test_vectors/didcomm_messages/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

TEST_MESSAGE = Message(
id="1234567890",
thid="1234567890",
type="https://example.com/protocols/lets_do_lunch/1.0/proposal",
frm=ALICE_DID,
to=[BOB_DID],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
TEST_PLAINTEXT_DIDCOMM_MESSAGE_SIMPLE = json_dumps(
{
"id": "1234567890",
"thid": "1234567890",
"typ": "application/didcomm-plain+json",
"type": "https://example.com/protocols/lets_do_lunch/1.0/proposal",
"from": "did:example:alice",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@

TEST_SIGNED_DIDCOMM_MESSAGE_ALICE_KEY_1 = json_dumps(
{
"payload": "eyJpZCI6IjEyMzQ1Njc4OTAiLCJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLXBsYWluK2pzb24iLCJ0eXBlIjoiaHR0cDovL2V4YW1wbGUuY29tL3Byb3RvY29scy9sZXRzX2RvX2x1bmNoLzEuMC9wcm9wb3NhbCIsImZyb20iOiJkaWQ6ZXhhbXBsZTphbGljZSIsInRvIjpbImRpZDpleGFtcGxlOmJvYiJdLCJjcmVhdGVkX3RpbWUiOjE1MTYyNjkwMjIsImV4cGlyZXNfdGltZSI6MTUxNjM4NTkzMSwiYm9keSI6eyJtZXNzYWdlc3BlY2lmaWNhdHRyaWJ1dGUiOiJhbmQgaXRzIHZhbHVlIn19",
"payload": "eyJ0eXBlIjoiaHR0cDovL2V4YW1wbGUuY29tL3Byb3RvY29scy9sZXRzX2RvX2x1bmNoLzEuMC9wcm9wb3NhbCIsImJvZHkiOnsibWVzc2FnZXNwZWNpZmljYXR0cmlidXRlIjoiYW5kIGl0cyB2YWx1ZSJ9LCJpZCI6IjEyMzQ1Njc4OTAiLCJ0byI6WyJkaWQ6ZXhhbXBsZTpib2IiXSwiY3JlYXRlZF90aW1lIjoxNTE2MjY5MDIyLCJleHBpcmVzX3RpbWUiOjE1MTYzODU5MzEsInRoaWQiOiIxMjM0NTY3ODkwIiwiZnJvbSI6ImRpZDpleGFtcGxlOmFsaWNlIiwidHlwIjoiYXBwbGljYXRpb24vZGlkY29tbS1wbGFpbitqc29uIn0",
"signatures": [
{
"protected": "eyJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLXNpZ25lZCtqc29uIiwiYWxnIjoiRWREU0EifQ",
"signature": "FW33NnvOHV0Ted9-F7GZbkia-vYAfBKtH4oBxbrttWAhBZ6UFJMxcGjL3lwOl4YohI3kyyd08LHPWNMgP2EVCQ",
"signature": "D3aEM0sIPobl-qnJh7kF1Hol6Wz_CKmyqHfgwGbFKmjyWvUoXhCI09ZiE5qmJlyyPo_ubqEqWOiPYcroEjJ9Ag",
"header": {"kid": "did:example:alice#key-1"},
}
],
Expand All @@ -20,11 +20,11 @@

TEST_SIGNED_DIDCOMM_MESSAGE_ALICE_KEY_2 = json_dumps(
{
"payload": "eyJpZCI6IjEyMzQ1Njc4OTAiLCJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLXBsYWluK2pzb24iLCJ0eXBlIjoiaHR0cDovL2V4YW1wbGUuY29tL3Byb3RvY29scy9sZXRzX2RvX2x1bmNoLzEuMC9wcm9wb3NhbCIsImZyb20iOiJkaWQ6ZXhhbXBsZTphbGljZSIsInRvIjpbImRpZDpleGFtcGxlOmJvYiJdLCJjcmVhdGVkX3RpbWUiOjE1MTYyNjkwMjIsImV4cGlyZXNfdGltZSI6MTUxNjM4NTkzMSwiYm9keSI6eyJtZXNzYWdlc3BlY2lmaWNhdHRyaWJ1dGUiOiJhbmQgaXRzIHZhbHVlIn19",
"payload": "eyJ0eXBlIjoiaHR0cDovL2V4YW1wbGUuY29tL3Byb3RvY29scy9sZXRzX2RvX2x1bmNoLzEuMC9wcm9wb3NhbCIsImJvZHkiOnsibWVzc2FnZXNwZWNpZmljYXR0cmlidXRlIjoiYW5kIGl0cyB2YWx1ZSJ9LCJpZCI6IjEyMzQ1Njc4OTAiLCJ0byI6WyJkaWQ6ZXhhbXBsZTpib2IiXSwiY3JlYXRlZF90aW1lIjoxNTE2MjY5MDIyLCJleHBpcmVzX3RpbWUiOjE1MTYzODU5MzEsInRoaWQiOiIxMjM0NTY3ODkwIiwiZnJvbSI6ImRpZDpleGFtcGxlOmFsaWNlIiwidHlwIjoiYXBwbGljYXRpb24vZGlkY29tbS1wbGFpbitqc29uIn0",
"signatures": [
{
"protected": "eyJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLXNpZ25lZCtqc29uIiwiYWxnIjoiRVMyNTYifQ",
"signature": "gcW3lVifhyR48mLHbbpnGZQuziskR5-wXf6IoBlpa9SzERfSG9I7oQ9pssmHZwbvJvyMvxskpH5oudw1W3X5Qg",
"signature": "YktGGyR1i23DGdMz0_yhrb5o4zLKja3VT-qKMFD2VhIX2Vb12SkbD2kREc6HZE-NcIBXmpTGu1P-HtWRp-Ys0g",
"header": {"kid": "did:example:alice#key-2"},
}
],
Expand All @@ -33,11 +33,11 @@

TEST_SIGNED_DIDCOMM_MESSAGE_ALICE_KEY_3 = json_dumps(
{
"payload": "eyJpZCI6IjEyMzQ1Njc4OTAiLCJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLXBsYWluK2pzb24iLCJ0eXBlIjoiaHR0cDovL2V4YW1wbGUuY29tL3Byb3RvY29scy9sZXRzX2RvX2x1bmNoLzEuMC9wcm9wb3NhbCIsImZyb20iOiJkaWQ6ZXhhbXBsZTphbGljZSIsInRvIjpbImRpZDpleGFtcGxlOmJvYiJdLCJjcmVhdGVkX3RpbWUiOjE1MTYyNjkwMjIsImV4cGlyZXNfdGltZSI6MTUxNjM4NTkzMSwiYm9keSI6eyJtZXNzYWdlc3BlY2lmaWNhdHRyaWJ1dGUiOiJhbmQgaXRzIHZhbHVlIn19",
"payload": "eyJ0eXBlIjoiaHR0cDovL2V4YW1wbGUuY29tL3Byb3RvY29scy9sZXRzX2RvX2x1bmNoLzEuMC9wcm9wb3NhbCIsImJvZHkiOnsibWVzc2FnZXNwZWNpZmljYXR0cmlidXRlIjoiYW5kIGl0cyB2YWx1ZSJ9LCJpZCI6IjEyMzQ1Njc4OTAiLCJ0byI6WyJkaWQ6ZXhhbXBsZTpib2IiXSwiY3JlYXRlZF90aW1lIjoxNTE2MjY5MDIyLCJleHBpcmVzX3RpbWUiOjE1MTYzODU5MzEsInRoaWQiOiIxMjM0NTY3ODkwIiwiZnJvbSI6ImRpZDpleGFtcGxlOmFsaWNlIiwidHlwIjoiYXBwbGljYXRpb24vZGlkY29tbS1wbGFpbitqc29uIn0",
"signatures": [
{
"protected": "eyJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLXNpZ25lZCtqc29uIiwiYWxnIjoiRVMyNTZLIn0",
"signature": "EGjhIcts6tqiJgqtxaTiTY3EUvL-_rLjn9lxaZ4eRUwa1-CS1nknZoyJWbyY5NQnUafWh5nvCtQpdpMyzH3blw",
"signature": "mYLZAUz97lodaWbwED6IvBf9_8Zj41-dM6r_3gTE9AkVZI3AJlz-agDBNSmNHd3CjRKW1KNS10pND57123b9aA",
"header": {"kid": "did:example:alice#key-3"},
}
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
TEST_PLAINTEXT_DIDCOMM_MESSAGE_MINIMAL = json_dumps(
{
"id": "1234567890",
"thid": "1234567890",
"typ": "application/didcomm-plain+json",
"type": "https://example.com/protocols/lets_do_lunch/1.0/proposal",
"body": {},
Expand All @@ -34,6 +35,7 @@
TEST_PLAINTEXT_ATTACHMENT_BASE64 = json_dumps(
{
"id": "1234567890",
"thid": "1234567890",
"typ": "application/didcomm-plain+json",
"type": "https://example.com/protocols/lets_do_lunch/1.0/proposal",
"from": "did:example:alice",
Expand All @@ -48,6 +50,7 @@
TEST_PLAINTEXT_ATTACHMENT_LINKS = json_dumps(
{
"id": "1234567890",
"thid": "1234567890",
"typ": "application/didcomm-plain+json",
"type": "https://example.com/protocols/lets_do_lunch/1.0/proposal",
"from": "did:example:alice",
Expand All @@ -64,6 +67,7 @@
TEST_PLAINTEXT_ATTACHMENT_JSON = json_dumps(
{
"id": "1234567890",
"thid": "1234567890",
"typ": "application/didcomm-plain+json",
"type": "https://example.com/protocols/lets_do_lunch/1.0/proposal",
"from": "did:example:alice",
Expand All @@ -80,6 +84,7 @@
TEST_PLAINTEXT_ATTACHMENT_MULTI_1 = json_dumps(
{
"id": "1234567890",
"thid": "1234567890",
"typ": "application/didcomm-plain+json",
"type": "https://example.com/protocols/lets_do_lunch/1.0/proposal",
"from": "did:example:alice",
Expand All @@ -97,6 +102,7 @@
TEST_PLAINTEXT_ATTACHMENT_MULTI_2 = json_dumps(
{
"id": "1234567890",
"thid": "1234567890",
"typ": "application/didcomm-plain+json",
"type": "https://example.com/protocols/lets_do_lunch/1.0/proposal",
"from": "did:example:alice",
Expand All @@ -115,6 +121,7 @@
TEST_PLAINTEXT_ACKS = json_dumps(
{
"id": "1234567890",
"thid": "1234567890",
"typ": "application/didcomm-plain+json",
"type": "https://example.com/protocols/lets_do_lunch/1.0/proposal",
"from": "did:example:alice",
Expand Down
Empty file added tests/unit/didcomm/__init__.py
Empty file.
26 changes: 26 additions & 0 deletions tests/unit/didcomm/message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import pytest
import attr

from didcomm.core import converters
from didcomm.message import GenericMessage


@pytest.mark.parametrize(
"m_id, m_id_expected",
[
pytest.param("123", "123", id="str"),
pytest.param(lambda: "345", "345", id="function"),
pytest.param(None, None, id="default"),
],
)
def test_forward_message__id_good(m_id, m_id_expected, mocker):
if m_id is None:
# XXX mocks in-place of imports doesn't work for attrs calsses
# by some reason
spy = mocker.spy(converters, "didcomm_id_generator_default")
msg = GenericMessage(type="test_type", body="test_body")
assert spy.call_count == 1
assert msg.id == spy.spy_return
else:
msg = GenericMessage(type="test_type", body="test_body")
assert attr.evolve(msg, **dict(id=m_id)).id == m_id_expected
21 changes: 0 additions & 21 deletions tests/unit/didcomm/protocols/routing/test_forward_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from typing import Callable

from didcomm.errors import DIDCommValueError, MalformedMessageError
from didcomm.core import converters
from didcomm.core.types import DIDCOMM_ORG_DOMAIN, DIDCommFields
from didcomm.message import Attachment, AttachmentDataJson, AttachmentDataLinks
from didcomm.protocols.routing.forward import (
Expand Down Expand Up @@ -33,26 +32,6 @@ def test_forward_message__id_bad(m_id, fwd_msg):
attr.evolve(fwd_msg, **dict(id=m_id))


@pytest.mark.parametrize(
"m_id, m_id_expected",
[
pytest.param("123", "123", id="str"),
pytest.param(lambda: "345", "345", id="function"),
pytest.param(None, None, id="default"),
],
)
def test_forward_message__id_good(m_id, m_id_expected, mocker, fwd_msg):
if m_id is None:
# XXX mocks in-place of imports doesn't work for attrs calsses
# by some reason
spy = mocker.spy(converters, "didcomm_id_generator_default")
msg = gen_fwd_msg()
assert spy.call_count == 1
assert msg.id == spy.spy_return
else:
assert attr.evolve(fwd_msg, **dict(id=m_id)).id == m_id_expected


def _build_mturi(
scheme="https",
domain=DIDCOMM_ORG_DOMAIN,
Expand Down
3 changes: 0 additions & 3 deletions tests/unit/pack_common/test_message_negative.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ def update_msg_field(field_name, value):

@pytest.mark.asyncio
async def test_no_required_param(resolvers_config_alice):
with pytest.raises(TypeError):
Message(type="https://example.com/protocols/lets_do_lunch/1.0/proposal", body={})
with pytest.raises(TypeError):
Message(
id="1234567890",
Expand Down Expand Up @@ -129,7 +127,6 @@ async def test_custom_header_equals_to_default(msg, resolvers_config_alice):
@pytest.mark.parametrize(
"msg",
[
update_msg_field("id", 123),
update_msg_field("type", 123),
update_msg_field("frm", 123),
update_msg_field("to", 123),
Expand Down

0 comments on commit ac4696c

Please sign in to comment.