-
Notifications
You must be signed in to change notification settings - Fork 836
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add canvases APIs and users.discoverableContacts.lookup API #1508
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
import logging | ||
import os | ||
import time | ||
import unittest | ||
|
||
from integration_tests.env_variable_names import SLACK_SDK_TEST_BOT_TOKEN | ||
from integration_tests.helpers import async_test | ||
from slack_sdk.web import WebClient | ||
from slack_sdk.web.async_client import AsyncWebClient | ||
|
||
|
||
class TestWebClient(unittest.TestCase): | ||
"""Runs integration tests with real Slack API""" | ||
|
||
def setUp(self): | ||
self.logger = logging.getLogger(__name__) | ||
self.bot_token = os.environ[SLACK_SDK_TEST_BOT_TOKEN] | ||
self.sync_client: WebClient = WebClient(token=self.bot_token) | ||
self.async_client: AsyncWebClient = AsyncWebClient(token=self.bot_token) | ||
|
||
def tearDown(self): | ||
pass | ||
|
||
def test_sync(self): | ||
client = self.sync_client | ||
|
||
# Channel canvas | ||
new_channel = client.conversations_create(name=f"test-{str(time.time()).replace('.', '-')}") | ||
channel_id = new_channel["channel"]["id"] | ||
channel_canvas = client.conversations_canvases_create( | ||
channel_id=channel_id, | ||
document_content={ | ||
"type": "markdown", | ||
"markdown": """# My canvas | ||
--- | ||
## Hey | ||
What's up? | ||
""", | ||
}, | ||
) | ||
self.assertIsNone(channel_canvas.get("error")) | ||
|
||
# Standalone canvas | ||
standalone_canvas = client.canvases_create( | ||
title="My canvas", | ||
document_content={ | ||
"type": "markdown", | ||
"markdown": """# My canvas | ||
--- | ||
## Hey | ||
What's up? | ||
""", | ||
}, | ||
) | ||
self.assertIsNone(standalone_canvas.get("error")) | ||
canvas_id = standalone_canvas.get("canvas_id") | ||
|
||
sections = client.canvases_sections_lookup(canvas_id=canvas_id, criteria={"contains_text": "Hey"}) | ||
section_id = sections["sections"][0]["id"] | ||
|
||
edit = client.canvases_edit( | ||
canvas_id=canvas_id, | ||
changes=[ | ||
{ | ||
"operation": "replace", | ||
"section_id": section_id, | ||
"document_content": {"type": "markdown", "markdown": "## Hey Hey"}, | ||
} | ||
], | ||
) | ||
self.assertIsNone(edit.get("error")) | ||
|
||
user_id = client.auth_test()["user_id"] | ||
access_set = client.canvases_access_set( | ||
canvas_id=canvas_id, | ||
access_level="write", | ||
user_ids=[user_id], | ||
) | ||
self.assertIsNone(access_set.get("error")) | ||
|
||
access_delete = client.canvases_access_delete(canvas_id=canvas_id, user_ids=[user_id]) | ||
self.assertIsNone(access_delete.get("error")) | ||
|
||
delete = client.canvases_delete(canvas_id=canvas_id) | ||
self.assertIsNone(delete.get("error")) | ||
|
||
@async_test | ||
async def test_async(self): | ||
client = self.async_client | ||
|
||
# Channel canvas | ||
new_channel = await client.conversations_create(name=f"test-{str(time.time()).replace('.', '-')}") | ||
channel_id = new_channel["channel"]["id"] | ||
channel_canvas = await client.conversations_canvases_create( | ||
channel_id=channel_id, | ||
document_content={ | ||
"type": "markdown", | ||
"markdown": """# My canvas | ||
--- | ||
## Hey | ||
What's up? | ||
""", | ||
}, | ||
) | ||
self.assertIsNone(channel_canvas.get("error")) | ||
|
||
# Standalone canvas | ||
standalone_canvas = await client.canvases_create( | ||
title="My canvas", | ||
document_content={ | ||
"type": "markdown", | ||
"markdown": """# My canvas | ||
--- | ||
## Hey | ||
What's up? | ||
""", | ||
}, | ||
) | ||
self.assertIsNone(standalone_canvas.get("error")) | ||
canvas_id = standalone_canvas.get("canvas_id") | ||
|
||
sections = await client.canvases_sections_lookup(canvas_id=canvas_id, criteria={"contains_text": "Hey"}) | ||
section_id = sections["sections"][0]["id"] | ||
|
||
edit = await client.canvases_edit( | ||
canvas_id=canvas_id, | ||
changes=[ | ||
{ | ||
"operation": "replace", | ||
"section_id": section_id, | ||
"document_content": {"type": "markdown", "markdown": "## Hey Hey"}, | ||
} | ||
], | ||
) | ||
self.assertIsNone(edit.get("error")) | ||
|
||
user_id = (await client.auth_test())["user_id"] | ||
access_set = await client.canvases_access_set( | ||
canvas_id=canvas_id, | ||
access_level="write", | ||
user_ids=[user_id], | ||
) | ||
self.assertIsNone(access_set.get("error")) | ||
|
||
access_delete = await client.canvases_access_delete(canvas_id=canvas_id, user_ids=[user_id]) | ||
self.assertIsNone(access_delete.get("error")) | ||
|
||
delete = await client.canvases_delete(canvas_id=canvas_id) | ||
self.assertIsNone(delete.get("error")) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2244,6 +2244,107 @@ | |
) | ||
return await self.api_call("calls.update", http_verb="POST", params=kwargs) | ||
|
||
async def canvases_create( | ||
self, | ||
*, | ||
title: Optional[str] = None, | ||
document_content: Dict[str, str], | ||
**kwargs, | ||
) -> AsyncSlackResponse: | ||
"""Create Canvas for a user | ||
https://api.slack.com/methods/canvases.create | ||
""" | ||
kwargs.update({"title": title, "document_content": json.dumps(document_content)}) | ||
return await self.api_call("canvases.create", params=kwargs) | ||
|
||
async def canvases_edit( | ||
self, | ||
*, | ||
canvas_id: str, | ||
changes: Sequence[Dict[str, Any]], | ||
**kwargs, | ||
) -> AsyncSlackResponse: | ||
"""Update an existing canvas | ||
https://api.slack.com/methods/canvases.edit | ||
""" | ||
kwargs.update({"canvas_id": canvas_id, "changes": json.dumps(changes)}) | ||
return await self.api_call("canvases.edit", params=kwargs) | ||
|
||
async def canvases_delete( | ||
self, | ||
*, | ||
canvas_id: str, | ||
**kwargs, | ||
) -> AsyncSlackResponse: | ||
"""Deletes a canvas | ||
https://api.slack.com/methods/canvases.delete | ||
""" | ||
kwargs.update({"canvas_id": canvas_id}) | ||
return await self.api_call("canvases.delete", params=kwargs) | ||
|
||
async def canvases_access_set( | ||
self, | ||
*, | ||
canvas_id: str, | ||
access_level: str, | ||
channel_ids: Optional[Union[Sequence[str], str]] = None, | ||
user_ids: Optional[Union[Sequence[str], str]] = None, | ||
**kwargs, | ||
) -> AsyncSlackResponse: | ||
"""Sets the access level to a canvas for specified entities | ||
https://api.slack.com/methods/canvases.access.set | ||
""" | ||
kwargs.update({"canvas_id": canvas_id, "access_level": access_level}) | ||
if channel_ids is not None: | ||
if isinstance(channel_ids, (list, Tuple)): | ||
kwargs.update({"channel_ids": ",".join(channel_ids)}) | ||
else: | ||
kwargs.update({"channel_ids": channel_ids}) | ||
if user_ids is not None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a note as a possible enhancement in the future, I see that both client and async client handle this list serialization into comma-separated strings at the method level. Perhaps an enhancement could be to factor that out into a utility method? I see the same pattern is repeated a lot:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah it should be good; we can do it in the future |
||
if isinstance(user_ids, (list, Tuple)): | ||
kwargs.update({"user_ids": ",".join(user_ids)}) | ||
else: | ||
kwargs.update({"user_ids": user_ids}) | ||
|
||
return await self.api_call("canvases.access.set", params=kwargs) | ||
|
||
async def canvases_access_delete( | ||
self, | ||
*, | ||
canvas_id: str, | ||
channel_ids: Optional[Union[Sequence[str], str]] = None, | ||
user_ids: Optional[Union[Sequence[str], str]] = None, | ||
**kwargs, | ||
) -> AsyncSlackResponse: | ||
"""Create a Channel Canvas for a channel | ||
https://api.slack.com/methods/canvases.access.delete | ||
""" | ||
kwargs.update({"canvas_id": canvas_id}) | ||
if channel_ids is not None: | ||
if isinstance(channel_ids, (list, Tuple)): | ||
kwargs.update({"channel_ids": ",".join(channel_ids)}) | ||
else: | ||
kwargs.update({"channel_ids": channel_ids}) | ||
if user_ids is not None: | ||
if isinstance(user_ids, (list, Tuple)): | ||
kwargs.update({"user_ids": ",".join(user_ids)}) | ||
else: | ||
kwargs.update({"user_ids": user_ids}) | ||
return await self.api_call("canvases.access.delete", params=kwargs) | ||
|
||
async def canvases_sections_lookup( | ||
self, | ||
*, | ||
canvas_id: str, | ||
criteria: Dict[str, Any], | ||
**kwargs, | ||
) -> AsyncSlackResponse: | ||
"""Find sections matching the provided criteria | ||
https://api.slack.com/methods/canvases.sections.lookup | ||
""" | ||
kwargs.update({"canvas_id": canvas_id, "criteria": json.dumps(criteria)}) | ||
return await self.api_call("canvases.sections.lookup", params=kwargs) | ||
|
||
# -------------------------- | ||
# Deprecated: channels.* | ||
# You can use conversations.* APIs instead. | ||
|
@@ -3113,6 +3214,19 @@ | |
kwargs.update({"channel": channel}) | ||
return await self.api_call("conversations.unarchive", params=kwargs) | ||
|
||
async def conversations_canvases_create( | ||
self, | ||
*, | ||
channel_id: str, | ||
document_content: Dict[str, str], | ||
seratch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
**kwargs, | ||
) -> AsyncSlackResponse: | ||
"""Create a Channel Canvas for a channel | ||
https://api.slack.com/methods/conversations.canvases.create | ||
""" | ||
kwargs.update({"channel_id": channel_id, "document_content": json.dumps(document_content)}) | ||
return await self.api_call("conversations.canvases.create", params=kwargs) | ||
|
||
async def dialog_open( | ||
self, | ||
*, | ||
|
@@ -4931,6 +5045,17 @@ | |
kwargs.update({"presence": presence}) | ||
return await self.api_call("users.setPresence", params=kwargs) | ||
|
||
async def users_discoverableContacts_lookup( | ||
self, | ||
email: str, | ||
**kwargs, | ||
) -> AsyncSlackResponse: | ||
"""Lookup an email address to see if someone is on Slack | ||
https://api.slack.com/methods/users.discoverableContacts.lookup | ||
""" | ||
seratch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
kwargs.update({"email": email}) | ||
return await self.api_call("users.discoverableContacts.lookup", params=kwargs) | ||
|
||
async def users_profile_get( | ||
self, | ||
*, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The API mentions
document_content
is optional - should this be wrapped w/Optional
, then?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, thanks but I don't want to encourage skipping the parameter because the current set of public APIs does not allow you to append sections if the document does not have anything. Your canvas document should have at least one section (like h1 title) for the following operations.