Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 118 additions & 1 deletion integration_tests/web/test_message_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,32 @@
import os
import time
import unittest
import json

from integration_tests.env_variable_names import SLACK_SDK_TEST_BOT_TOKEN
from slack_sdk.models.metadata import Metadata
from slack_sdk.models.metadata import (
Metadata,
EventAndEntityMetadata,
EntityMetadata,
ExternalRef,
EntityPayload,
EntityAttributes,
EntityTitle,
TaskEntityFields,
EntityStringField,
EntityTitle,
EntityAttributes,
EntityFullSizePreview,
TaskEntityFields,
EntityTypedField,
EntityStringField,
EntityTimestampField,
EntityEditSupport,
EntityEditTextConfig,
EntityCustomField,
EntityUserIDField,
ExternalRef,
)
from slack_sdk.web import WebClient


Expand Down Expand Up @@ -125,3 +148,97 @@ def test_publishing_message_metadata_using_models(self):
)
self.assertIsNone(scheduled.get("error"))
self.assertIsNotNone(scheduled.get("message").get("metadata"))

def test_publishing_entity_metadata(self):
client: WebClient = WebClient(token=self.bot_token)
new_message = client.chat_postMessage(
channel="C014KLZN9M0",
text="Message with entity metadata",
metadata={
"entities": [
{
"entity_type": "slack#/entities/task",
"url": "https://abc.com/123",
"external_ref": {"id": "123"},
"entity_payload": {
"attributes": {"title": {"text": "My task"}, "product_name": "We reference only"},
"fields": {
"due_date": {"value": "2026-06-06", "type": "slack#/types/date", "edit": {"enabled": True}},
"created_by": {"type": "slack#/types/user", "user": {"user_id": "U014KLZE350"}},
"date_created": {"value": 1760629278},
},
"custom_fields": [
{
"label": "img",
"key": "img",
"type": "slack#/types/image",
"image_url": "https://s3-media2.fl.yelpcdn.com/bphoto/korel-1YjNtFtJlMTaC26A/o.jpg",
}
],
},
}
]
},
)

self.assertIsNone(new_message.get("error"))
self.assertIsNone(new_message.get("warning"))

def test_publishing_entity_metadata_using_models(self):
# Build the metadata

title = EntityTitle(text="My title")
full_size_preview = EntityFullSizePreview(
is_supported=True,
preview_url="https://s3-media3.fl.yelpcdn.com/bphoto/c7ed05m9lC2EmA3Aruue7A/o.jpg",
mime_type="image/jpeg",
)
attributes = EntityAttributes(title=title, product_name="My Product", full_size_preview=full_size_preview)
description = EntityStringField(
value="Description of the task.",
long=True,
edit=EntityEditSupport(enabled=True, text=EntityEditTextConfig(min_length=5, max_length=100)),
)
due_date = EntityTypedField(value="2026-06-06", type="slack#/types/date", edit=EntityEditSupport(enabled=True))
created_by = EntityTypedField(
type="slack#/types/user",
user=EntityUserIDField(user_id="USLACKBOT"),
)
date_created = EntityTimestampField(value=1762450663, type="slack#/types/timestamp")
date_updated = EntityTimestampField(value=1762450663, type="slack#/types/timestamp")
fields = TaskEntityFields(
description=description,
due_date=due_date,
created_by=created_by,
date_created=date_created,
date_updated=date_updated,
)
custom_fields = []
custom_fields.append(
EntityCustomField(
label="My Image",
key="my-image",
type="slack#/types/image",
image_url="https://s3-media3.fl.yelpcdn.com/bphoto/c7ed05m9lC2EmA3Aruue7A/o.jpg",
)
)
entity = EntityPayload(attributes=attributes, fields=fields, custom_fields=custom_fields)

client: WebClient = WebClient(token=self.bot_token)
new_message = client.chat_postMessage(
channel="#random",
text="Message with entity metadata",
metadata=EventAndEntityMetadata(
entities=[
EntityMetadata(
entity_type="slack#/entities/task",
external_ref=ExternalRef(id="abc123"),
url="https://myappdomain.com",
entity_payload=entity,
)
]
),
)

self.assertIsNone(new_message.get("error"))
self.assertIsNone(new_message.get("warning"))
9 changes: 7 additions & 2 deletions slack_sdk/models/basic_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ def validate_json(self) -> None:
if callable(method) and hasattr(method, "validator"):
method()

def get_object_attribute(self, key: str):
return getattr(self, key, None)

Comment on lines +43 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 💯

def get_non_null_attributes(self) -> dict:
"""
Construct a dictionary out of non-null keys (from attributes property)
Expand All @@ -57,7 +60,7 @@ def to_dict_compatible(value: Union[dict, list, object, tuple]) -> Union[dict, l
return value

def is_not_empty(self, key: str) -> bool:
value = getattr(self, key, None)
value = self.get_object_attribute(key)
if value is None:
return False

Expand All @@ -75,7 +78,9 @@ def is_not_empty(self, key: str) -> bool:
return value is not None

return {
key: to_dict_compatible(getattr(self, key, None)) for key in sorted(self.attributes) if is_not_empty(self, key)
key: to_dict_compatible(value=self.get_object_attribute(key))
for key in sorted(self.attributes)
if is_not_empty(self, key)
}

def to_dict(self, *args) -> dict:
Expand Down
Loading