Repository: PrefectHQ/prefect

Issue:
Create Slack task for Task Library
It would be awesome to have a task in the Task Library that sent a slack message. Inputs could be for a `channel` or a `user`. Similar installation instructions to https://docs.prefect.io/guide/tutorials/slack-notifications.html#installation-instructions (or just a link to these instructions)


Discussion:

Resolving patch:
diff --git a/src/prefect/tasks/notifications/__init__.py b/src/prefect/tasks/notifications/__init__.py
--- a/src/prefect/tasks/notifications/__init__.py
+++ b/src/prefect/tasks/notifications/__init__.py
@@ -1 +1,7 @@
+"""
+Collection of tasks for sending notifications.
+
+Useful for situations in which state handlers are inappropriate.
+"""
 from prefect.tasks.notifications.email_task import EmailTask
+from prefect.tasks.notifications.slack_task import SlackTask
diff --git a/src/prefect/tasks/notifications/slack_task.py b/src/prefect/tasks/notifications/slack_task.py
new file mode 100644
--- /dev/null
+++ b/src/prefect/tasks/notifications/slack_task.py
@@ -0,0 +1,39 @@
+import requests
+from typing import Any, cast
+
+from prefect import Task
+from prefect.client import Secret
+from prefect.utilities.tasks import defaults_from_attrs
+
+
+class SlackTask(Task):
+    """
+    Task for sending a message via Slack.  For this task to function properly,
+    you must have the `"SLACK_WEBHOOK_URL"` Prefect Secret.  For installing the Prefect App,
+    please see these [installation instructions](https://docs.prefect.io/guide/tutorials/slack-notifications.html#installation-instructions).
+
+    Args:
+        - message (str, optional): the message to send; can also be provided at runtime
+        - **kwargs (Any, optional): additional keyword arguments to pass to the base Task initialization
+    """
+
+    def __init__(self, message: str = None, **kwargs: Any):
+        self.message = message
+        super().__init__(**kwargs)
+
+    @defaults_from_attrs("message")
+    def run(self, message: str = None) -> None:
+        """
+        Run method which sends a Slack message.
+
+        Args:
+            - message (str, optional): the message to send; if not provided here, will use the value provided
+                at initialization
+
+        Returns:
+            - None
+        """
+
+        webhook_url = cast(str, Secret("SLACK_WEBHOOK_URL").get())
+        r = requests.post(webhook_url, json={"text": message})
+        r.raise_for_status()


Test patch:
diff --git a/tests/tasks/notifications/test_slack_task.py b/tests/tasks/notifications/test_slack_task.py
new file mode 100644
--- /dev/null
+++ b/tests/tasks/notifications/test_slack_task.py
@@ -0,0 +1,29 @@
+from unittest.mock import MagicMock
+
+import pytest
+
+from prefect import context
+from prefect.tasks.notifications import SlackTask
+from prefect.utilities.configuration import set_temporary_config
+
+
+class TestInitialization:
+    def test_inits_with_no_args(self):
+        t = SlackTask()
+        assert t
+
+    def test_kwargs_get_passed_to_task_init(self):
+        t = SlackTask(name="bob", checkpoint=True, tags=["foo"])
+        assert t.name == "bob"
+        assert t.checkpoint is True
+        assert t.tags == {"foo"}
+
+    def test_webhook_url_pulled_from_secrets(self, monkeypatch):
+        req = MagicMock()
+        monkeypatch.setattr("prefect.tasks.notifications.slack_task.requests", req)
+        t = SlackTask()
+        with set_temporary_config({"cloud.use_local_secrets": True}):
+            with context({"secrets": dict(SLACK_WEBHOOK_URL="http://foo/bar")}):
+                res = t.run(message="o hai mark")
+        assert req.post.call_args[0] == ("http://foo/bar",)
+        assert req.post.call_args[1]["json"]["text"] == "o hai mark"