Skip to content

Commit 9054dec

Browse files
authored
Migrate prefect-slack to core (#12839)
1 parent 0ee3f6d commit 9054dec

File tree

15 files changed

+510
-2
lines changed

15 files changed

+510
-2
lines changed

.github/workflows/integration-tests.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ on:
33
pull_request:
44
paths:
55
- .github/workflows/integration-tests.yaml
6-
- "**/*.py"
6+
- "src/prefect/**/*.py"
77
- requirements.txt
88
- requirements-client.txt
99
- requirements-dev.txt
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
collectionName: prefect-slack
22
author: Prefect
33
authorUrl: https://prefect.io
4-
documentation: https://prefecthq.github.io/prefect-slack/
4+
documentation: https://docs.prefect.io/integrations/prefect-slack/
55
iconUrl: /img/collections/slack.png
66
tag: Slack
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
::: prefect_slack.credentials
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# prefect-slack
2+
3+
## Welcome!
4+
5+
`prefect-slack` is a collection of prebuilt Prefect tasks that can be used to quickly construct Prefect flows.
6+
7+
## Getting Started
8+
9+
### Python setup
10+
11+
Requires an installation of Python 3.8+
12+
13+
We recommend using a Python virtual environment manager such as pipenv, conda or virtualenv.
14+
15+
These tasks are designed to work with Prefect 2.0. For more information about how to use Prefect, please refer to the [Prefect documentation](https://docs.prefect.io/).
16+
17+
### Installation
18+
19+
Install `prefect-slack`
20+
21+
```bash
22+
pip install prefect-slack
23+
```
24+
25+
### Slack setup
26+
27+
In order to use tasks in the collection, you'll first need to create an Slack app and install it in your Slack workspace. You can create a Slack app by navigating to the [apps page](https://api.slack.com/apps) for your Slack account and selecting 'Create New App'.
28+
29+
For tasks that require a Bot user OAuth token, you can get a token for your app by navigating to your apps __OAuth & Permissions__ page.
30+
31+
For tasks that require and Webhook URL, you get generate new Webhook URLs by navigating to you apps __Incoming Webhooks__ page.
32+
33+
Slack's [Basic app setup](https://api.slack.com/authentication/basics) guide provides additional details on setting up a Slack app.
34+
35+
### Write and run a flow
36+
37+
```python
38+
from prefect import flow
39+
from prefect.context import get_run_context
40+
from prefect_slack import SlackCredentials
41+
from prefect_slack.messages import send_chat_message
42+
43+
44+
@flow
45+
def example_send_message_flow():
46+
context = get_run_context()
47+
48+
# Run other tasks and subflows here
49+
50+
token = "xoxb-your-bot-token-here"
51+
send_chat_message(
52+
slack_credentials=SlackCredentials(token),
53+
channel="#prefect",
54+
text=f"Flow run {context.flow_run.name} completed :tada:"
55+
)
56+
57+
example_send_message_flow()
58+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
::: prefect_slack.messages

mkdocs.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ nav:
254254
- Jobs: integrations/prefect-databricks/jobs.md
255255
- Rest: integrations/prefect-databricks/rest.md
256256
- Flows: integrations/prefect-databricks/flows.md
257+
- Slack:
258+
- integrations/prefect-slack/index.md
259+
- Credentials: integrations/prefect-slack/credentials.md
260+
- Messages: integrations/prefect-slack/messages.md
257261
- API Reference:
258262
- api-ref/index.md
259263
- Python SDK:
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# prefect-slack
2+
3+
## Welcome!
4+
5+
`prefect-slack` is a collection of prebuilt Prefect tasks that can be used to quickly construct Prefect flows.
6+
7+
## Getting Started
8+
9+
### Python setup
10+
11+
Requires an installation of Python 3.8+
12+
13+
We recommend using a Python virtual environment manager such as pipenv, conda or virtualenv.
14+
15+
These tasks are designed to work with Prefect 2.0. For more information about how to use Prefect, please refer to the [Prefect documentation](https://docs.prefect.io/).
16+
17+
### Installation
18+
19+
Install `prefect-slack`
20+
21+
```bash
22+
pip install prefect-slack
23+
```
24+
25+
### Slack setup
26+
27+
In order to use tasks in the collection, you'll first need to create an Slack app and install it in your Slack workspace. You can create a Slack app by navigating to the [apps page](https://api.slack.com/apps) for your Slack account and selecting 'Create New App'.
28+
29+
For tasks that require a Bot user OAuth token, you can get a token for your app by navigating to your apps __OAuth & Permissions__ page.
30+
31+
For tasks that require and Webhook URL, you get generate new Webhook URLs by navigating to you apps __Incoming Webhooks__ page.
32+
33+
Slack's [Basic app setup](https://api.slack.com/authentication/basics) guide provides additional details on setting up a Slack app.
34+
35+
### Write and run a flow
36+
37+
```python
38+
from prefect import flow
39+
from prefect.context import get_run_context
40+
from prefect_slack import SlackCredentials
41+
from prefect_slack.messages import send_chat_message
42+
43+
44+
@flow
45+
def example_send_message_flow():
46+
context = get_run_context()
47+
48+
# Run other tasks and subflows here
49+
50+
token = "xoxb-your-bot-token-here"
51+
send_chat_message(
52+
slack_credentials=SlackCredentials(token),
53+
channel="#prefect",
54+
text=f"Flow run {context.flow_run.name} completed :tada:"
55+
)
56+
57+
example_send_message_flow()
58+
```
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from . import _version
2+
from .credentials import SlackCredentials, SlackWebhook # noqa
3+
4+
__version__ = _version.__version__
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""Credential classes used to perform authenticated interacting with Slack"""
2+
from dataclasses import dataclass
3+
4+
from slack_sdk.web.async_client import AsyncWebClient
5+
from slack_sdk.webhook.async_client import AsyncWebhookClient
6+
7+
8+
@dataclass
9+
class SlackCredentials:
10+
"""
11+
Class for holding Slack credentials for use in tasks and flows.
12+
13+
Args:
14+
token: Bot user OAuth token for the Slack app used to perform actions.
15+
"""
16+
17+
token: str
18+
19+
def get_client(self) -> AsyncWebClient:
20+
"""
21+
Returns an authenticated `AsyncWebClient` to interact with the Slack API.
22+
"""
23+
return AsyncWebClient(token=self.token)
24+
25+
26+
@dataclass
27+
class SlackWebhook:
28+
"""
29+
Class holding a Slack webhook for use in tasks and flows.
30+
31+
Args:
32+
url: Slack webhook URL which can be used to send messages
33+
(e.g. `https://hooks.slack.com/XXX`).
34+
"""
35+
36+
url: str
37+
38+
def get_client(self) -> AsyncWebhookClient:
39+
"""
40+
Returns and authenticated `AsyncWebhookClient` to interact with the configured
41+
Slack webhook.
42+
"""
43+
return AsyncWebhookClient(url=self.url)
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
"""Tasks for sending Slack messages"""
2+
3+
from typing import TYPE_CHECKING, Dict, Optional, Sequence, Union
4+
5+
from prefect import get_run_logger, task
6+
from prefect_slack.credentials import SlackCredentials, SlackWebhook
7+
8+
if TYPE_CHECKING: # pragma: no cover
9+
import slack_sdk.models.attachments
10+
import slack_sdk.models.blocks
11+
12+
13+
@task
14+
async def send_chat_message(
15+
channel: str,
16+
slack_credentials: SlackCredentials,
17+
text: Optional[str] = None,
18+
attachments: Optional[
19+
Sequence[Union[Dict, "slack_sdk.models.attachments.Attachment"]]
20+
] = None,
21+
slack_blocks: Optional[
22+
Sequence[Union[Dict, "slack_sdk.models.blocks.Block"]]
23+
] = None,
24+
) -> Dict:
25+
"""
26+
Sends a message to a Slack channel
27+
28+
Args:
29+
channel: The name of the channel in which to post the chat message
30+
(e.g. #general)
31+
slack_credentials: Instance of `SlackCredentials` initialized with a Slack
32+
bot token
33+
text: Contents of the message. It's a best practice to always provide a `text`
34+
argument when posting a message. The `text` argument is used in places where
35+
content cannot be rendered such as: system push notifications, assistive
36+
technology such as screen readers, etc.
37+
attachments: List of objects defining secondary context in the posted Slack
38+
message. The [Slack API docs](https://api.slack.com/messaging/composing/layouts#building-attachments)
39+
provide guidance on building attachments.
40+
slack_blocks: List of objects defining the layout and formatting of the posted
41+
message. The [Slack API docs](https://api.slack.com/block-kit/building)
42+
provide guidance on building messages with blocks.
43+
44+
Returns:
45+
Dict: Response from the Slack API. Example response structures can be found in
46+
the [Slack API docs](https://api.slack.com/methods/chat.postMessage#examples)
47+
48+
Examples:
49+
Post a message at the end of a flow run
50+
51+
```python
52+
from prefect import flow
53+
from prefect.context import get_run_context
54+
from prefect_slack import SlackCredentials
55+
from prefect_slack.messages import send_chat_message
56+
57+
58+
@flow
59+
def example_send_message_flow():
60+
context = get_run_context()
61+
62+
# Run other tasks and subflows here
63+
64+
token = "xoxb-your-bot-token-here"
65+
send_chat_message(
66+
slack_credentials=SlackCredentials(token),
67+
channel="#prefect",
68+
text=f"Flow run {context.flow_run.name} completed :tada:"
69+
)
70+
71+
example_send_message_flow()
72+
```
73+
""" # noqa
74+
logger = get_run_logger()
75+
logger.info("Posting chat message to %s", channel)
76+
77+
client = slack_credentials.get_client()
78+
result = await client.chat_postMessage(
79+
channel=channel, text=text, blocks=slack_blocks, attachments=attachments
80+
)
81+
return result.data
82+
83+
84+
@task
85+
async def send_incoming_webhook_message(
86+
slack_webhook: SlackWebhook,
87+
text: Optional[str] = None,
88+
attachments: Optional[
89+
Sequence[Union[Dict, "slack_sdk.models.attachments.Attachment"]]
90+
] = None,
91+
slack_blocks: Optional[
92+
Sequence[Union[Dict, "slack_sdk.models.blocks.Block"]]
93+
] = None,
94+
) -> None:
95+
"""
96+
Sends a message via an incoming webhook
97+
98+
Args:
99+
slack_webhook: Instance of `SlackWebhook` initialized with a Slack
100+
webhook URL.
101+
text: Contents of the message. It's a best practice to always provide a `text`
102+
argument when posting a message. The `text` argument is used in places where
103+
content cannot be rendered such as: system push notifications, assistive
104+
technology such as screen readers, etc.
105+
attachments: List of objects defining secondary context in the posted Slack
106+
message. The [Slack API docs](https://api.slack.com/messaging/composing/layouts#building-attachments)
107+
provide guidance on building attachments.
108+
slack_blocks: List of objects defining the layout and formatting of the posted
109+
message. The [Slack API docs](https://api.slack.com/block-kit/building)
110+
provide guidance on building messages with blocks.
111+
112+
Examples:
113+
Post a message at the end of a flow run
114+
115+
```python
116+
from prefect import flow
117+
from prefect_slack import SlackWebhook
118+
from prefect_slack.messages import send_incoming_webhook_message
119+
120+
121+
@flow
122+
def example_send_message_flow():
123+
# Run other tasks and subflows here
124+
125+
webhook_url = "https://hooks.slack.com/XXX"
126+
send_incoming_webhook_message(
127+
slack_webhook=SlackWebhook(
128+
url=webhook_url
129+
),
130+
text="Warehouse loading flow completed :sparkles:"
131+
)
132+
133+
example_send_message_flow()
134+
```
135+
""" # noqa
136+
logger = get_run_logger()
137+
logger.info("Posting message to provided webhook")
138+
139+
client = slack_webhook.get_client()
140+
await client.send(text=text, attachments=attachments, blocks=slack_blocks)

0 commit comments

Comments
 (0)