Alexa for CI/CD: Putting it all together - Part 3
Tags: aws, serverless, cloud, alexa
Published: 2022-04-26 10:49:36

In the previous two parts in this series, we created our [Lambda function](https://dev.to/aws-builders/alexa-for-cicd-introduction-to-serverless-with-aws-lambda-and-bitrise-api-part-1-11ol) and the [Alexa Skill](https://dev.to/aws-builders/alexa-for-bitrise-cicd-introduction-to-amazon-alexa-and-alexa-skills-part-2-3ob0). In this tutorial, we’ll learn how to connect Amazon Alexa Skill to the AWS Lambda function to trigger Bitrise builds via a voice user interface.

Sounds good? Let’s get started!

## The solution

As we know AWS Lambda function needs to be triggered by any supported triggers. Because of this, we will use Alexa and Alexa Skill Kit (ASK) to trigger the function. Inside the function, we will add our logic to receive the voice command from Alexa and pass it to Bitrise API using the POST endpoint to trigger a new build in our Bitrise app. Then, we will get the response from Lambda to the Alexa as a JSON output. 

![1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cyy73swtlhjnkcgos5kk.png)

Previously our Lambda function was using the following code to trigger the build via the POST endpoint: 

```
var request = require('request');

let app_slug = process.env.APP_SLUG
let api_key = process.env.API_KEY

var options = {
  'method': 'POST',
  'url': 'https://api.bitrise.io/v0.1/apps/'+app_slug+'/builds',
  'headers': {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Authorization': api_key
  },
  body: JSON.stringify({
   'build_params': {
      'branch': 'chapter-3',
      'workflow_id': 'primary'
    },
    'hook_info': {
      'type': 'bitrise'
    }
  })

};

exports.handler =  async function(event, response) {
  console.log(response.body);
  return "done..."
}

request(options, function (error,response) {
  if (error) throw new Error(error);
  console.log(response.body);
});
```

But with the Alexa Skill Kit, we need to change this handler and the code logic  with something to work with the voice command and the Skill Kit by the following code: 

```
var request = require('request');

exports.handler = (event, context, callback) => {
    
    try {
        if (event.request.type === 'LaunchRequest') {
            callback(null, buildResponse('Hello from Bitrise'));
        } else if (event.request.type === 'IntentRequest') {
            const intentName = event.request.intent.name;
            
            if (intentName === 'triggerBuild') {
                
                buildBitriseApp(function (err, result) {
                    if(!err) callback(null, buildResponse('The build was launched! make sure you take a look.'));
                    else callback(null, buildResponse("Please check your build logs. Something went wrong."));
                });
            } else {
                callback(null, buildResponse("Sorry, i don't understand"));
            }
        } else if (event.request.type === 'SessionEndedRequest') {
            callback(null, buildResponse('Session Ended'));
        }
    } catch (e) {
        context.fail(`Exception: ${e}`);
    }
};

function buildResponse(response) {
    return {
        version: '1.0',
        response: {
            outputSpeech: {
                type: 'PlainText',
                text: response,
            },
            shouldEndSession: true,
        },
        sessionAttributes: {},
    };
}

let api_key = process.env.API_PERSONAL_KEY;
let app_slug = process.env.APP_SLUG;

function buildBitriseApp(callback) {
var options = {
  'method': 'POST',
  'url': 'https://api.bitrise.io/v0.1/apps/'+app_slug+'/builds',
  'headers': {
    'Authorization': api_key
  },
  body: JSON.stringify({
    "User-Agent": "alexa-skill",
    "hook_info": {
      "type": "bitrise"
    },
    "build_params": {
      "branch": 'main',
      "workflow_id": 'primary'
    }
  })
};
    request.post(options, function(error, response, body){
        if(error){
            callback("ERROR");

        } else {
            callback(null,"SUCCESS");
        }
    });
}
```

In the above code, we are specifying that when we launch the skill we’ll receive a message “Hello from Bitrise” and check if the intentName is “triggerBuild”.

We can call the function “buildResponse”, which calls the POST endpoint from Bitrise API with the payload that we added, specifying the following parameters: 

- API Key
- App Slug
- Branch Name
- Workflow ID

And we already added the **API Key** and the **App Slug** as Lambda Environment Variables. 

Click the **Deploy** button to save the changes. 

![2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qfaaw1tjp7vb0lswvwmp.gif)

Now it’s time to add the Alexa Skill Kit as a trigger for our Lambda function. 

Click on the **+ Add trigger** button in the **Function Overview** and the trigger configuration and select the **Alexa Skills kit**.

![3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i4uzptbo6lh2e7qbjrjf.png)

Then you can add your **Skill ID** and click the add button. You can get the Skill ID from the Endpoint menu in the Skill Kit build section.


![4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/itdop4s8seyr0azuixw3.png)


![5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r0scknu6x3qf8siv9r6d.png)

And now the trigger should be displayed like this:


![6](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4ubspr4lukuoxek1fxez.png)

On the other side, you need to add the Lambda ARN from the Lambda function page.


![7](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r8l4bqwtf26hb4y6walw.png)

And go to the Alexa Skill kit by opening the **Endpoint** option in the **build** section and pasting the **ARN** there, then clicking **Save Endpoint** and choosing **yes**.

After that, click on the **Code** button and you will see a notification that the default endpoint has changed and is no longer hosted by Alexa because it’s now on AWS and Lambda. 

![8](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3f2ycilwkuy3x58lnown.png)

## Test the integration between Lambda and Alexa

Now click on the **Test** button to go to the **Alexa Simulator** page.  And in the development write the following phrase: 

“**Alexa, open bitrise ci**” and click the **Enter** button 

![9](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/02hvetkzc8px0ipj16al.png)

To trigger a new build you can write: “Alexa, ask bitrise ci trigger a new bitrise build” and click the Enter button

![10](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5qon2xtkouhykdpnaihl.png)

Open your Bitrise [Dashboard](https://app.bitrise.io/dashboard) and, if the command ran successfully, you will notice that the build was triggered successfully and you can abort it after that to save your credits. 

![11](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rnmusvkdj2z5xki4ylf0.png)

We did it! Congratulations you have successfully triggered your first build using Bitrise API, Lambda via Alexa Skill kit. 

Amazing! 

## Specifying the git branch name and the workflow name using Slots

You may notice that in our Lambda function we are using hardcoded values like the branch name and the workflow name as build parameters.

```
"build_params": {
      "branch": 'main',
      "workflow_id": 'primary'
    }
```
But what if I need to use different values... Can I do that? 

Of course, you can!

In the first part, we mentioned that Alexa Skill has a key feature which is Slots and you can use them as variables that you provide with a name and a slot type for each slot in your interaction model. 

Let’s do it then! 

In your Alexa Skills Console, click on **Build** then click on **Slot Types**, and click **Add Slot Type**.

![12](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dt9zfvgwztlp9eouflbe.png)

- Add **BranchName** and click the **Next** button 
- Enter default values such as **main** and **master**

![13](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/do6cztgukat489rz7ofl.png)

- Add another **Slot** **WorkflowName** click the **Next** button
- Enter default values such as **deploy** and **primary**

![14](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fx78lsotg0wzu347e54d.png)

Click on **Intents** then **triggerBuild** intent 
Change the **Sample Utterance** to be like the following:

![15](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hgywa53kmcgqwiky0wx9.png)

The Slots will be added under Intent Slots and make sure that the Slot Type is what we created previously.

![16](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/89enwquszwab94na83zp.png)

```
var request = require('request');

var workflowSlot;
var branchSlot;

exports.handler = (event, context, callback) => {
    
    try {
        if (event.request.type === 'LaunchRequest') {
            callback(null, buildResponse('Hello from Bitrise'));
        } else if (event.request.type === 'IntentRequest') {
            const intentName = event.request.intent.name;
            workflowSlot = event.request.intent.slots.WorkflowName.value;
            branchSlot = event.request.intent.slots.BranchName.value;
            
            if (intentName === 'triggerBuild') {
                
                buildBitriseApp(function (err, result) {
                    if(!err) callback(null, buildResponse('The build was launched with '+ workflowSlot +' workflow for '+ branchSlot + ' branch. make sure you take a look.'));
                    else callback(null, buildResponse("Please check your build logs. Something went wrong."));
                });
            } else {
                callback(null, buildResponse("Sorry, i don't understand"));
            }
        } else if (event.request.type === 'SessionEndedRequest') {
            callback(null, buildResponse('Session Ended'));
        }
    } catch (e) {
        context.fail(`Exception: ${e}`);
    }
};

function buildResponse(response) {
    return {
        version: '1.0',
        response: {
            outputSpeech: {
                type: 'PlainText',
                text: response,
            },
            shouldEndSession: true,
        },
        sessionAttributes: {},
    };
}

let api_key = process.env.API_PERSONAL_KEY;
let app_slug = process.env.APP_SLUG;

function buildBitriseApp(callback) {
var options = {
  'method': 'POST',
  'url': 'https://api.bitrise.io/v0.1/apps/'+app_slug+'/builds',
  'headers': {
    'Authorization': api_key
  },
  body: JSON.stringify({
    "User-Agent": "alexa-skill",
    "hook_info": {
      "type": "bitrise"
    },
    "build_params": {
      "branch": branchSlot,
      "workflow_id": workflowSlot
    }
  })
};
    request.post(options, function(error, response, body){
        if(error){
            callback("ERROR");

        } else {
            callback(null,"SUCCESS");
        }
    });
}
```

In the above code we added these values:

```
var workflowSlot;
var branchSlot;

workflowSlot = event.request.intent.slots.WorkflowName.value;
branchSlot = event.request.intent.slots.BranchName.value;

And in the build parameters we replace the hardcoded values: 
"build_params": {
      "branch": branchSlot,
      "workflow_id": workflowSlot
    }
```

Click **Deploy** to save the changes and now you can test your Alexa Skills Kit.

In the **Alexa Simulator** write the following phrase:

**“Alexa, ask bitrise ci trigger build for primary workflow for main branch”**

**Note:** You should have a workflow and branch name with the same values otherwise the build will not be triggered

I will try to use different apps and specify the workflow name and the branch name as well.


![16](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/19s4m4y6wx3m8yxaurlm.gif)

## Monitor AWS Lambda with CloudWatch

You can also check the runtime metrics for your functions on Amazon CloudWatch. The metrics shown are an aggregate view of all function runtime activity.

![17](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ai36fdn3h58l7v61jzk.png)

## What’s next?

In the next article, we will **distribute** our Alexa Skills Kit to be able to use in our **AWS organization**.