Reacting to events raised by Octopus
Tags: devops, twilio, octopus, twiliohackathon
Published: 2020-04-18 16:48:40

---
title: Reacting to events raised by Octopus
published: true
description: Let's create the initial cloud function for Octwilio. It is triggered by an Octopus subscription and sends a request to the Twilio API.
tags: devops,twilio,octopus,twiliohackathon
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/038x7hhoezeff378e9v0.jpg
series: Building Octwilio
---

## Song recommendation of the day <a name="song"></a>

This song played while I was testing the function. I love the original, and this is a well-done cover.
{% spotify spotify:track:28jVDx5pXc7TMYgSgbJmMO %}

## The plan <a name="the-plan"></a>

[In the last post](https://dev.to/octopus/octwilio-combining-the-octopus-and-twilio-apis-for-twiliohackathon-1846), I cut my teeth on the Twilio API. I successfully sent a text message and even got a shiny, new [Octopus Step Template](https://library.octopus.com/step-templates/3c3904a9-d08c-4f18-b86c-0304800bb541/actiontemplate-twilio-send-sms-(powershell)) out of it.

Today, I set up a Firebase cloud function that accepts an event from Octopus when a deployment requires approval.

## Configuring Firebase <a name="configuring-firebase"></a>

[Firebase](https://firebase.google.com/) is an application development platform with a large number of offerings. Firebase can host your application and database. It also provides features like authentication and A/B testing.

I followed the [Getting Started](https://firebase.google.com/docs/functions/get-started) guide for Cloud Functions. These steps included creating a new project, updating the Firebase CLI on my machine, and calling `firebase init` to configure the project locally.

I initialized the project with only the functions module for now.

## Adding my settings to the Firebase configuration <a name="firebase-config"></a>

You can set [environment configuration settings](https://firebase.google.com/docs/functions/config-env]) to your application with the Firebase CLI.

I set up a few settings that I need in my function. Real values have been replaced to protect ~~the innocent~~ me.

```
firebase functions:config:set octwilio.octopus.token="This is a token provided by Octopus"
firebase functions:config:set octwilio.twilio.account_sid="The account ID from Twilio"
firebase functions:config:set octwilio.twilio.auth_token="The auth token from Twilio"
firebase functions:config:set octwilio.twilio.approval.from_number="+15555555555"
firebase functions:config:set octwilio.twilio.approval.to_number="+15555555555"
```

## Coding the function <a name="coding-the-function"></a>

I based this function on one I wrote for adding [Slack notifications for all production deployments](https://dev.to/octopus/adding-notifications-for-every-production-deployment-in-octopus-deploy-g5i).

Let us start with our cloud function definition. It is a function triggered by an HTTPS request. It is named `approvalRaised`. I can activate it with a request to `https://us-central1-octwilio.cloudfunctions.net/approvalRaised`.

```javascript
exports.approvalRaised = functions.https.onRequest((req, res) => {
    return authorizeRequest(req, res)
        .then(getPayload)
        .then(sendTwilioMessage)
        .then(() => { return res.status(200).send(); });
});
```

It takes the request and passes it through a pipeline of functions.

The first one, `authorizeRequest`, checks for a secret token in the header. The function returns an unauthorized response if the token is incorrect or missing.

```javascript
function authorizeRequest(req) {
    const providedToken = req.get('octwilio-token');
    const token = functions.config().octwilio.octopus.token;

    if (!providedToken || providedToken !== token) {
        return Promise.reject({
            code: 401,
            message: 'Missing or invalid token'
        });
    }

    return Promise.resolve(req);
}
```

The `getPayload` function checks for the subscription payload from Octopus and passes it to the next function. The function returns a bad request response if the payload is missing.

```javascript
function getPayload(req) {
    const payload = req.body.Payload;

    if (payload) {
        console.log(payload);
        return Promise.resolve(payload);
    }

    return Promise.reject({
        code: 400,
        message: 'No payload provided'
    });
}
```

The `sendTwilioMessage` function sends a message through Twilio.

The message it is sending is static for now. I will address that in the next post.

The details needed to make the request are pulled from the Firebase configuration. My secrets are safe!

```javascript
function sendTwilioMessage() {
    let config = functions.config().octwilio;
    let client = new twilio(config.twilio.account_sid, config.twilio.auth_token);

    return client.messages.create({
        body: 'An approval is required',
        to: config.twilio.approval.to_number,
        from: config.twilio.approval.from_number
    }).then((message) => console.log(message.sid));
}
```

The code was at a good point to test. I published the function to Firebase with the following command:

```
firebase deploy --only functions
```

## Creating and testing the subscription <a name="subscription"></a>

[Subscriptions](https://octopus.com/docs/administration/managing-infrastructure/subscriptions) let you act on events raised from Octopus Deploy. You can configure an email notification with the target event details included. You can also send the event payload to a webhook.

I configured my subscription to trigger on `Manual intervention interruption raised` events. The subscription sends the payload to my `approvalRaised` cloud function and includes the secret token in the header.

![Approval Required subscription in Octopus](https://dev-to-uploads.s3.amazonaws.com/i/0jtq9jkw5d6avtv3x8rs.png)

I added a manual intervention step to my project. Then I created a new release and deployed it to the Development environment.

Approval required!

![The deployment is waiting for approval](https://dev-to-uploads.s3.amazonaws.com/i/fj2ckv78kqh3770y7evv.png)

## Outbound requests from Firebase Cloud Functions <a name="outbound-requests"></a>

I should have received a text message for this, but I didn't.

I checked the logs for the function in Firebase and immediately saw the issue.

```
Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions
```

You cannot make external network requests on the free Firebase plan. I changed my plan to the pay-as-you-go tier and tried again.

![A message from Twilio that approval is required](https://dev-to-uploads.s3.amazonaws.com/i/76jcfb58xs58ry8aeqql.png)

Success!

## Next time <a name="next-time"></a>

Next time, I will add some of the deployment details into the message and handle a reply to the message.

_Cover photo by [Isabel Galvez](https://unsplash.com/@isagalvezphoto) on [Unsplash](https://unsplash.com/)._