Login or sign up to IBM Cloud
Once logged in, in the top search bar enter cloud functions and select the option with the ƒ symbol.
This will take you to the Functions dashboard. Select Actions from the side bar and then select Create.
Here you will have 5 boxes to choose from - select Action.
Give the Action:
- A name
- Select the Runtime (Go 1.15)
- Leave the package as
Default Package
For the purpose of this workshop Go is the runtime as there is no ready made Twilio package in IBM Cloud Functions and the request code needs to be manually written.
Feel free to mix it up and choose a language you are more familiar with. (Also included in this workshop is the Python and Node.JS equivalent). The principals of this workshop are very much the same across the board.
As it stands this Action is not public and this prevents Webhooks and other public HTTP actions from interacting with it (as seen with annotation 1 below). To change this you need to make it a Web Action. Select Endpoints from the side bar.
The next step is to select the checkbox Enable as Web Action and click Save. You will notice the Web Action icon change and you will be able to see the public HTTP URL.
Now the Action is public and we can hit the endpoint from external sources, we need to set up some parameters for the code to use. This is essentially an environment variable for the function.
For this, you will need to add the following:
recipientNumber - The number you wish to send a text message too
authToken - Your Twilio Account Auth Token (Found on your dashboard)
accountSid - Your Twilio Account SID (Found on your dashboard)
twilioNumber - Your number associated with your Twilio Account
Before going any further, carefully read the code below line by line to understand what is happening (use the comments to help).
If you are not using Go, I would still recommend reading this code to understand what is going on behind the scenes in the other language packages - Python / Node.JS.
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
)
/* Main is the function implementing the action
** It is accepting "params" which is map of strings
** The function then returns a map of strings */
func Main(params map[string]interface{}) map[string]interface{} {
// extract the parameters from the params map
action := params["action"].(string)
twilioNumber := params["twilioNumber"].(string)
recipientNumber := params["recipientNumber"].(string)
// only invoke Twilio message service if the GitHub Pull Request action = assigned
if action == "assigned" {
fmt.Println("pull request assigned")
// set account information by extracting data from params map
accountSid := params["accountSid"].(string)
authToken := params["authToken"].(string)
urlStr := "https://api.twilio.com/2010-04-01/Accounts/" + accountSid + "/Messages.json"
// build the text message being sent to the recipient
textMsg := "New pull request assignee"
// package up the data values
msgData := url.Values{}
msgData.Set("To", recipientNumber)
msgData.Set("From", twilioNumber)
msgData.Set("Body", textMsg)
msgDataReader := *strings.NewReader(msgData.Encode())
// call the request function with the data provided
msg := request(authToken, accountSid, urlStr, msgDataReader)
return msg
}
fmt.Println("Pull request action = ", action)
msg := make(map[string]interface{})
msg["action"] = action
// return the output JSON
return msg
}
func request(authToken, accountSid, urlStr string, msgDataReader strings.Reader) map[string]interface{} {
// create a HTTP client, request & set request headers
client := &http.Client{}
req, _ := http.NewRequest("POST", urlStr, &msgDataReader)
req.SetBasicAuth(accountSid, authToken)
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
// HTTP POST request and return message SID to the console
resp, _ := client.Do(req)
var msg = make(map[string]interface{})
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
var data map[string]interface{}
decoder := json.NewDecoder(resp.Body)
err := decoder.Decode(&data)
if err == nil {
fmt.Println(data["sid"])
msg["status"] = "SMS sent"
}
} else {
fmt.Println(resp.Status)
msg["status"] = "SMS not sent"
}
return msg
}action := params["action"].(string)
We haven't specified an action parameter for our function in the previous step. The reason we haven't manually specified this is because it will be within the payload of the POST request that hits this endpoint. It is still a parameter, just not one that we need to set.
Let me explain.
When the GitHub Webhook sends a POST request to this endpoint, it will carry with it a payload of information. This payload will tell us much more information about the event GitHub.
Visit GitHub Webhook events for more information about each event payload.
This workshop is using the pull_request event. You can see here, the key is action and it can have many values. In the code snippet above, we are making decisions based on the value of action. This determines whether or not an SMS is sent.
First we will check the function fails correctly.
The code to send a text message notification should only run if the value of action == assigned. All other cases should fail gracefully. It will return a message to the console and return an object with the action that was supplied in the parameters.
You should see that the output is what we expected. A simple log to the console of the action and the return object containing the action. No other code was executed as the conditions were not met.
We can see it is now failing successfully. Lets see if we can make it pass correctly and send a text message.
To do this, we need to change it parameters we are invoking the Action with. Change test to assigned and then invoke it again.
The action works! Now what?
This currently runs manually, so we need to create an automatic trigger.
Before we get started with this step, make sure you have you a GitHub repository you can test on. If you don't, don't panic. Just checkout this repository tutorial which shows you how to create your own. (This only takes ~2 minutes to do).
A Trigger will listen for an event to happen from inside or outside of IBM Cloud Functions and invoke any connected Actions.
First, head back to the Functions dashboard and select Triggers on the side panel and click on Create.
You will be faced with the same 5 options as earlier, except in this case, select Trigger.
This will lead you to a page with a few more options around what type of trigger events we can listen for. For this workshop we are listening to GitHub Webhook events so select GitHub.
Setting up the trigger steps:
- Click on
Get Access Token- Clicking on this button will promp you to authenticate with GitHub if this is your first time authenticating or it will automatically get an Auth Token if it not your first time. Either way, this just gives GitHub permission to interact with theTrigger. Trigger Nameis how it will be referencedUsername- This is your GitHub Username and should populate automatically.Repository- This will be the repository with the Webhook attached to it and the repository that you wish to interact with the trigger. Select one from the dropdown list (preferably the one you created to test this workshop on).Events- This is a comma seperated list and is GitHub specific. TheTriggerwill only listen for the Events you specify. A full list of Events can be found on the GitHub Webhook docs. For this, enterpull_requestas this is the event we want to listen for.- Click
Create
Wait for the Trigger to create and load.
After it has been created, you will need to connect the Action created in the previous steps.
Since we have already made an Action, navigate to the Select Existing tab and find the Action from the dropdown list and click on Add.
If you head over to the GitHub repository connected to the trigger, you should see the Webhook attached to the repository buy looking in Settings -> Webhooks of the repository on GitHub.
Head to the testing repository you created and edit the README.md
🚨 🚨 Make a change to the code and then make sure you check the button Create a new branch for this commit and start a pull request 🚨 🚨
This will take you through to opening a new pull request. Give it a title (and a description if you wish) and then click Create new pull request.
The final piece to this puzzle is selecting a new assignee. For the purpose of this workshop, you can just assign yourself. This will be enough to create an event and trigger your serverless function.
If everything has been set up correctly, you should:
- Receive a text from Twilio (this may take a minute).
and
- Be able to see the logs in the
Activations Dashboard.
Wahoo! You have finished this IBM Cloud Functions workshop.
⚡ I challenge you to extend this workshop and show us what you create via Twitter at @CodeMeetup. ⚡























