|
| 1 | +# Writing a Webhook to Slack Bridge |
| 2 | + |
| 3 | +This Bridge connects an HTTP endpoint to Slack. Every time the webhook is called a message will be produced, which we will validate and transform into an Slack message. |
| 4 | + |
| 5 | +We will be calling the exposed HTTP endpoint using `curl`, in a real world scenario the caller would be an application configuring a webhook callback. |
| 6 | + |
| 7 | +## Events |
| 8 | + |
| 9 | +Webhook Source produce arbitrary events based on configuration and received requests. |
| 10 | + |
| 11 | +- `type` attribute is set to the one configured by user. |
| 12 | +- `source` attribute is set to the one configured by user. |
| 13 | +- `datacontenttype` is set to the `Content-Type` received at the incoming request. |
| 14 | +- `data` is set to the body of the received request. |
| 15 | + |
| 16 | +Slack Target expect one of these 3 event types along with their related payload: |
| 17 | + |
| 18 | +- `com.slack.webapi.chat.postMessage` for consuming [chat.postMessage][chat.postMessage] |
| 19 | +- `com.slack.webapi.chat.scheduleMessage` for consuming [chat.scheduleMessage][chat.scheduleMessage] |
| 20 | +- `com.slack.webapi.chat.update` for consuming [chat.update][chat.update] |
| 21 | + |
| 22 | +This fictional scenario will send the following data to the Webhook Source using `curl`. |
| 23 | +```json |
| 24 | +{"message": "Hello Slack!"} |
| 25 | +``` |
| 26 | + |
| 27 | +The Webhook Source is expected to produce this event. |
| 28 | + |
| 29 | +```json |
| 30 | +type: webhook.slack.postmessage |
| 31 | +data: {"message": "Hello Slack!"} |
| 32 | +others: ... |
| 33 | +``` |
| 34 | + |
| 35 | +We will be using TriggerMesh's Function to perform a transformation which will consume the event above and produce this one. |
| 36 | + |
| 37 | +```json |
| 38 | +type: com.slack.webapi.chat.postMessage |
| 39 | +data: {"channel":"ABCDE12345", "text": "Hello Slack!"} |
| 40 | +others: ... |
| 41 | +``` |
| 42 | + |
| 43 | +This event will finally be consumed by the Slack Target that will in turn call the Slack API to post the message. |
| 44 | + |
| 45 | +## Webhook Source |
| 46 | + |
| 47 | +For simplicity we are setting up a non authenticated Webhook and using the default Kubernetes namespace. |
| 48 | + |
| 49 | +```yaml |
| 50 | +apiVersion: sources.triggermesh.io/v1alpha1 |
| 51 | +kind: WebhookSource |
| 52 | +metadata: |
| 53 | + name: post-message |
| 54 | +spec: |
| 55 | + eventType: webhook.slack.postmessage |
| 56 | + eventSource: webhoock.post-message |
| 57 | + |
| 58 | + sink: |
| 59 | + ref: |
| 60 | + apiVersion: eventing.knative.dev/v1 |
| 61 | + kind: Broker |
| 62 | + name: default |
| 63 | +``` |
| 64 | +
|
| 65 | +The `eventType` and `eventSource` CloudEvents attributes are being set for further event filtering. There is a reference to a Broker sink object where events will be sent, we will get to that one later. |
| 66 | + |
| 67 | +## Slack Target |
| 68 | + |
| 69 | +Slack Target requires: |
| 70 | + |
| 71 | +- Creating a new [Slack App][slack-apps]: add the `chat:write` permission under `Bot Token Scopes`, then install the application at your workspace. |
| 72 | +- A Slack API token: from the **Install App** menu retrieve the OAuth Access token that begins with `xoxb-`. |
| 73 | + |
| 74 | +Create a secret using the Slack API token |
| 75 | + |
| 76 | +```yaml |
| 77 | +apiVersion: v1 |
| 78 | +kind: Secret |
| 79 | +metadata: |
| 80 | + name: slack-tm |
| 81 | +type: Opaque |
| 82 | +stringData: |
| 83 | + token: xoxb-12345-abcde |
| 84 | +``` |
| 85 | + |
| 86 | +Create the Slack Target referencing the API token. |
| 87 | + |
| 88 | +```yaml |
| 89 | +apiVersion: targets.triggermesh.io/v1alpha1 |
| 90 | +kind: SlackTarget |
| 91 | +metadata: |
| 92 | + name: slack-tm |
| 93 | +spec: |
| 94 | + token: |
| 95 | + secretKeyRef: |
| 96 | + name: slack-tm |
| 97 | + key: token |
| 98 | +``` |
| 99 | + |
| 100 | +## Transformation Component |
| 101 | + |
| 102 | +To bridge the gap between the event produced by the Webhook Source and the one expected at the Slack Target we need to perform a transformation, which we can do using a declarative or coded approach. We will use the later here. |
| 103 | + |
| 104 | +Replace the channel ID in this transformation with the one you want to use. The channel ID can be retrieved from the URL either at the browser or selecting the `copy link` option at the Slack app. Please make sure that the bot user is added to the Slack channel. |
| 105 | + |
| 106 | +```yaml |
| 107 | +apiVersion: extensions.triggermesh.io/v1alpha1 |
| 108 | +kind: Function |
| 109 | +metadata: |
| 110 | + name: webhook-to-slack |
| 111 | +spec: |
| 112 | + runtime: python |
| 113 | + public: false |
| 114 | + entrypoint: transformToSlack |
| 115 | + ceOverrides: |
| 116 | + extensions: |
| 117 | + type: com.slack.webapi.chat.postMessage |
| 118 | + code: | |
| 119 | + from random import randrange |
| 120 | +
|
| 121 | + def transformToSlack(event, context): |
| 122 | + return { |
| 123 | + "channel":"REPLACE-CHANNEL-ID", |
| 124 | + "text": event['message'] |
| 125 | + } |
| 126 | +``` |
| 127 | + |
| 128 | +## Routing Components |
| 129 | + |
| 130 | +In order to connect all components we will setup these elements: |
| 131 | + |
| 132 | +- A central Broker that receives messages from the Source |
| 133 | +- A Trigger that consumes Webhook events filtered by the `webhook.slack.postmessage` type and sends them to the transformation Function. |
| 134 | +- A Trigger that consumes transformed events filtered by the `com.slack.webapi.chat.postMessage` type and sends them to the Slack target. |
| 135 | + |
| 136 | +The Broker name is set to `default` to match the one used at the Webhook Source earlier. |
| 137 | + |
| 138 | +```yaml |
| 139 | +apiVersion: eventing.knative.dev/v1 |
| 140 | +kind: Broker |
| 141 | +metadata: |
| 142 | + name: default |
| 143 | +``` |
| 144 | + |
| 145 | +Both Triggers are setup on the Broker and subscribe their corresponding destination filtering by types. |
| 146 | + |
| 147 | +```yaml |
| 148 | +apiVersion: eventing.knative.dev/v1 |
| 149 | +kind: Trigger |
| 150 | +metadata: |
| 151 | + name: wehook-to-transform |
| 152 | +spec: |
| 153 | + broker: default |
| 154 | + filter: |
| 155 | + attributes: |
| 156 | + type: webhook.slack.postmessage |
| 157 | + subscriber: |
| 158 | + ref: |
| 159 | + apiVersion: extensions.triggermesh.io/v1alpha1 |
| 160 | + kind: Function |
| 161 | + name: webhook-to-slack |
| 162 | +
|
| 163 | +--- |
| 164 | +
|
| 165 | +apiVersion: eventing.knative.dev/v1 |
| 166 | +kind: Trigger |
| 167 | +metadata: |
| 168 | + name: slack-post-messages |
| 169 | +spec: |
| 170 | + broker: default |
| 171 | + filter: |
| 172 | + attributes: |
| 173 | + type: com.slack.webapi.chat.postMessage |
| 174 | + subscriber: |
| 175 | + ref: |
| 176 | + apiVersion: targets.triggermesh.io/v1alpha1 |
| 177 | + kind: SlackTarget |
| 178 | + name: slack-tm |
| 179 | +``` |
| 180 | + |
| 181 | +## Done |
| 182 | + |
| 183 | +Retrieve the URL where the Webhook is listening for incoming requests. |
| 184 | + |
| 185 | +```console |
| 186 | +$ kubectl get webhooksources.sources.triggermesh.io post-message |
| 187 | +NAME READY REASON URL SINK AGE |
| 188 | +post-message True https://webhooksource-post-message.woodford.dev.triggermesh.io http://broker-ingress.knative-eventing.svc.cluster.local/woodford/default 61s |
| 189 | +
|
| 190 | +Use `curl` or any HTTP capable client to post messages at Slack. |
| 191 | + |
| 192 | +``` |
| 193 | +curl -d '{"message":"test my bridge"}' https://webhooksource-post-message.woodford.dev.triggermesh.io |
| 194 | +``` |
| 195 | +
|
| 196 | +Thid Bridge can be extended in many different ways: |
| 197 | +
|
| 198 | +- Validation and error handling at the transformation Function. |
| 199 | +- The Channel could be provided as a parameter from the WebhookSource, defaulting to a channel provided by the Function. |
| 200 | +- AWS Comprehend could be used for sentiment analysis. |
| 201 | +- Messages could be enriched if they contain word `avocado` with 🥑 |
| 202 | +- Add a Twilio Source that will also feed incoming messages to the Broker. |
| 203 | +- Add a Datadog Target that will convert a subset of filtered messages into alerts. |
| 204 | +
|
| 205 | +If you have any questions on how to build this Bridge or how to modify it to suit your needs, join our [Community Slack](http://triggermesh-community.slack.com/) and [contact us](mailto:[email protected]). |
| 206 | +
|
| 207 | +[chat.postMessage]: https://api.slack.com/methods/chat.postMessage |
| 208 | +[chat.scheduleMessage]: https://api.slack.com/methods/chat.scheduleMessage |
| 209 | +[chat.update]: https://api.slack.com/methods/chat.update |
| 210 | +[slack-apps]: https://api.slack.com/apps |
| 211 | +
|
0 commit comments