Code Monkey home page Code Monkey logo

itserverless's Introduction

Hi there 👋, my name is Thien

Software Development Major | Kahlert School of Computing | University of Utah

Spotify Playing 🎧

Spotify Playing

What I'm doing right now 🧰

  • 🔭 Working on my Portfolio Website!
  • 🌱 Learning React / Typescript / E2E testing
  • 🎓 Learning Algorithms, Models of Computation, and Database Systems in School!

Skills

My Skills

About me 😎

  • 🕹️ Interests: Gaming, Web Development, AI Art, Hiking, Composing Music, Bouldering, Esports, Hackathons, Competitive Programming, Photoshop
  • 📫 How to reach me: Email or LinkedIn
  • 😄 Pronouns: He/Him/His

Let's connect :)

github  linkedin  stackoverflow  twitter

GitHub Stats

GitHub Streak

Top Langs

itserverless's People

Contributors

counselorbot[bot] avatar thiennguyen2002 avatar

Watchers

 avatar

itserverless's Issues

Building Bunnimage: The Frontend

Week 4 Step 1 ⬤◯◯◯◯◯◯ | 🕐 Estimated completion: 20-25 minutes

Adding an Event Listener to your HTML Form!

Demo: 🐰

Congrats! If you made it this far, your Azure functions can now return data. However, users won't want to make POST requests with Postman to use your API though, so let's make it more user friendly.

✅ Task:

  • 1: Create a bunnimage-frontend branch
  • 2: Install LiveServer for testing
  • 3: Optional Learn HTML basics with the interactive course
  • 4: Write JavaScript to listen for the form to be submitted and call a function
  • 5: Write a function in index.js to display input appended with a ❤️ in the output div
  • 6: Commit your code to bunnimage/index.html and bunnimage/script.js
  • 7: Create a pull request that merges bunnimage-frontend to main, and only merge the pull request when the bot approves your changes!

🚧 Test your Work

Use LiveServer! This is a helpful VSCode extension that allows you to see your HTML that updates while you edit it.

❓ How do you use LiveServer?

image

  • To start a local server, click Go live at the bottom right of the screen, as shown in the image.
    • Make sure that you have the entire repo open on VS Code and not just the individual files.
    • If this is your first time installing LiveServer, you might need to close/quit VS Code and reopen it.
  • Test it out, and see what your HTML page looks like! It's OK if it's boring, so feel free to style it with CSS!

‼️ Create & Reference index.js in index.html

❓ How do I reference index.js in index.html?

Put it at the very end of the body tag!

    <script src="index.js" type="text/javascript"></script>

1: Writing the Event Handler Function!

On websites, there are many different types of events that can occur. For example:

  • The user selects a certain element or hovers the cursor over a certain element.
  • The user chooses a key on the keyboard.
  • A form is submitted.
  • Curious for more? Look here

We need to create a function which triggers when someone submits an image,
but first we will create a function which changes content on the page when we
submit text.

I'm confused on how to do this
  • In a new file named index.js, create a variable using the id of the HTML form:
const bunnForm = document.getElementById('bunnForm');
  • In your index.js file, create an event listener with an anonymous function.
    Inside of the function, create a variable for the value that was input in
    the text box.
bunnForm.addEventListener('submit', function (event) {
  const username = document.getElementById("username").value
});

💡 We are retrieving the value of the "username" text box with this code!
💡 The document.getElementById() method gets the value of the input text from the HTML.

  • Create a variable to target html div with the id of "output" and use the
    textContent property to add a "❤" to the value that was input for username.

💡 By default, the website will reload whenever a user clicks submit. This leads to our text on the page that we set being cleared away. We can prevent this by adding this to the top of our function.

event.preventDefault()
bunnForm.addEventListener('submit', function (event) {
   event.preventDefault()
   const username = document.getElementById("username").value
   const output = document.getElementById("output")
   output.textContent = username + "❤"
});

2: How do I trigger the function when the form is submitted?

The event listener triggers the JavaScript function when the form is submitted.
Here is how the form should look in the bunnimage index.html file:

<form id="bunnForm">
   <label for="name">Code: </label> <br>
   <input type="text" id="username" name="name" aria-label="name" /> <br>
   <input value="Submit" type="submit" />
</form>
<br/>
<div id="output"></div>

Here is the JavaScript which should be in a separate JavaScript file index.js:

const bunnForm = document.getElementById('bunnForm');

bunnForm.addEventListener('submit', function (event) {
   event.preventDefault()
   const username = document.getElementById("username").value
   const output = document.getElementById("output")
   output.textContent = username + "❤"
});

💡 the for attribute in the <label> and the aria-label attribute in the <input>element make the website more accessible to users needing assistive technologies such as screen readers.

📹 Walkthrough Video

walkthrough video

Start Course

Click the big green button that says Submit new issue, and you'll be good to go!

Showing off your twoCatz API

Week 4 Step 5 ⬤⬤⬤⬤⬤◯◯ | 🕐 Estimated completion: 5-20 minutes

Create the CataaS Website ~ meow

Demo: 🐱

Create a website that return a picture from the CatAAS API with the name of the cat being the input (each click should call the Cataas API)

✅ Task:

  • 1: Create a new twocatz-frontend branch
  • 2: Create an form page in a new HTML file called catzapp/index.html with a text input (type="text") and a button (type="button") that calls the Javascript function y1k3s() in twocatz/index.js
  • 3: Create a img object with id image in catzapp/index.html
  • 4: Write the y1k3s() function that replaces the img div with a picture from the CataaS API containing input from the text box
  • 5: Commit your changes to catzapp/index.html and catzapp/script.js to the twocatz-frontend branch
  • 6: Create a pull request that merges twocatz-frontend to main, and only merge the pull request when the bot approves your changes!

💡 Tip: It's a good idea to name your tags with id, since you'll then be able to use document.getElementById for modifying and getting values.

🚨 NOTE: The CounselorBot will be checking the code to see if you use the CataaS API endpoint. However, if the API is down, you may use the substitute version for testing purposes. Make sure to change it back when you submit your code!

🚧 Test your Work

Open up your LiveServer plugin and start your local server. To test your web app:

⚠️ When you enter "catzrule" into the textbox and click the button, is there a picture that appears with a cat and "catzrule" on it?

1: Creating an HTML template

In Visual Studio Code, you can create a html template containing the basic header tags by typing ! + Tab. This will create something like:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>
</html>

Everything that appers on the page will be in the <body> tags, and this is where you will be adding most (if not all of your html code.

2: Getting the Cat Pic

The img tag embeds an image, and it has attributes. We'll be using src.

❓ How can I modify src to get the picture?

When the button is clicked, it will call y1k3s(), so we will add a line of code in the function.
In HTML, the img tag looks like this:

<img src="img_girl.jpg" alt="Girl in a jacket" width="500" height="600">

We can change the src value to a URL, or a file on a server. In our case, we'll change it to a URL to the Cat API.

💡 Recall that the endpoint is https://cataas.com/cat/says/[your_text]

 document.getElementById("YOUR_IMAGE_ID").src = THE_ENDPOINT + THE_INPUT



📹 Walkthrough Video

walkthrough video

Continuing your work

Final Project Step 7 ⬤◯◯◯◯◯ | 🕐 Estimated completion: 30 mins

Continuing your work...

✅ Task:

  • Continue working in the final-project branch
  • Review all of the issues that are currently open:
    • Close the issues that you have completed
    • Add 3-4 more issues based on the work you plan on doing this week (if they are not already there)
    • Label the issues you want to work on as "Priority"
    • Label the issues that you need your mentor's help with as "mentor" and send them to your mentor to review before your meeting
  • 🚀 Commit any changes to the final-project branch, make a pull request, and merge it

Building Bunnimage: The Frontend

Week 4 Step 1 ⬤◯◯◯◯◯◯ | 🕐 Estimated completion: 20-25 minutes

Adding an Event Listener to your HTML Form!

Demo: 🐰

Congrats! If you made it this far, your Azure functions can now return data. However, users won't want to make POST requests with Postman to use your API though, so let's make it more user friendly.

✅ Task:

  • 1: Create a bunnimage-frontend branch
  • 2: Install LiveServer for testing
  • 3: Optional Learn HTML basics with the interactive course
  • 4: Write JavaScript to listen for the form to be submitted and call a function
  • 5: Write a function in index.js to display input appended with a ❤️ in the output div
  • 6: Commit your code to bunnimage/index.html and bunnimage/script.js
  • 7: Create a pull request that merges bunnimage-frontend to main, and only merge the pull request when the bot approves your changes!

🚧 Test your Work

Use LiveServer! This is a helpful VSCode extension that allows you to see your HTML that updates while you edit it.

❓ How do you use LiveServer?

image

  • To start a local server, click Go live at the bottom right of the screen, as shown in the image.
    • Make sure that you have the entire repo open on VS Code and not just the individual files.
    • If this is your first time installing LiveServer, you might need to close/quit VS Code and reopen it.
  • Test it out, and see what your HTML page looks like! It's OK if it's boring, so feel free to style it with CSS!

‼️ Create & Reference index.js in index.html

❓ How do I reference index.js in index.html?

Put it at the very end of the body tag!

    <script src="index.js" type="text/javascript"></script>

1: Writing the Event Handler Function!

On websites, there are many different types of events that can occur. For example:

  • The user selects a certain element or hovers the cursor over a certain element.
  • The user chooses a key on the keyboard.
  • A form is submitted.
  • Curious for more? Look here

We need to create a function which triggers when someone submits an image,
but first we will create a function which changes content on the page when we
submit text.

I'm confused on how to do this
  • In a new file named index.js, create a variable using the id of the HTML form:
const bunnForm = document.getElementById('bunnForm');
  • In your index.js file, create an event listener with an anonymous function.
    Inside of the function, create a variable for the value that was input in
    the text box.
bunnForm.addEventListener('submit', function (event) {
  const username = document.getElementById("username").value
});

💡 We are retrieving the value of the "username" text box with this code!
💡 The document.getElementById() method gets the value of the input text from the HTML.

  • Create a variable to target html div with the id of "output" and use the
    textContent property to add a "❤" to the value that was input for username.

💡 By default, the website will reload whenever a user clicks submit. This leads to our text on the page that we set being cleared away. We can prevent this by adding this to the top of our function.

event.preventDefault()
bunnForm.addEventListener('submit', function (event) {
   event.preventDefault()
   const username = document.getElementById("username").value
   const output = document.getElementById("output")
   output.textContent = username + "❤"
});

2: How do I trigger the function when the form is submitted?

The event listener triggers the JavaScript function when the form is submitted.
Here is how the form should look in the bunnimage index.html file:

<form id="bunnForm">
   <label for="name">Code: </label> <br>
   <input type="text" id="username" name="name" aria-label="name" /> <br>
   <input value="Submit" type="submit" />
</form>
<br/>
<div id="output"></div>

Here is the JavaScript which should be in a separate JavaScript file index.js:

const bunnForm = document.getElementById('bunnForm');

bunnForm.addEventListener('submit', function (event) {
   event.preventDefault()
   const username = document.getElementById("username").value
   const output = document.getElementById("output")
   output.textContent = username + "❤"
});

💡 the for attribute in the <label> and the aria-label attribute in the <input>element make the website more accessible to users needing assistive technologies such as screen readers.

📹 Walkthrough Video

walkthrough video

Getting Started with Serverless

Week 1 Step 1 ⬤◯◯◯◯◯◯◯◯ | 🕐 Estimated completion: 5-20 minutes

GitHub

This week, you will be going through steps to set up tools needed to be successful in this camp. If you are already familiar with some, feel free to skip to the end and complete the task to move on.

✅ Tasks:

  • 1: Optional: complete the get started with GitHub guide.
  • 2: Create a new branch named test.
  • 3: Add a paragraph introducing yourself under the About Me section in the blog.md file in root.
  • 4 Commit the change to test, make a draft pull request to your main branch, naming it Adding self introduction and add a detailed description of your contribution. Then, click Ready for Review and merge the pull request once the CounselorBot approves it!

💡 Tip: You will only need to make a draft pull request for Week 1 Step 1. For the rest of the curriculum, you may create "regular" pull requests!

Making a Draft Pull Request

When you're collaborating on GitHub with other developers, sometimes you might want to make a draft pull request before your code is ready to be reviewed! We can simulate this with the CounselorBot, and we will only be doing so with Week 1 Step 1.

Want a video demo? Click here!

Instead of clicking Create Pull Request, click the drop down arrow and click Create Draft Pull Request. Then, click Ready for Review. This allows the CounselorBot to review your pull request to check if you're ready to go!

What is GitHub?

GitHub is a industry-standard platform allows developers to save and collaborate on code. You can use GitHub to manage your files, changes in your project, version control (the ability to revert back to previous versions of your code as well as versions developed by other programmers), and more.

Check out "The Github Flow" for more information on issues, pull requests, committing, and branches!

If you want to learn more about what it is and how to use it, try taking this GitHub Learning Lab Course. After finishing it, you will have a strong understanding of all the features GitHub has to offer.

✍️Vocabulary

‼️ Repositories

Repositories (or repos) are essentially folders where you can store files of code. The repo of our camp was duplicated into your account when you clicked "Create Template" so that you can commit changes and complete each lesson.

‼️ Issues

For our camp, each week is placed inside an issue. Only when you complete the week (committing the necessary code and commenting), will the issue close and you can move on to the next issue. Don’t worry – committing changes is easier than it sounds.

💡 On usual repositories in the contributing world issues are tasks or bugs that need to be completed or fixed.

‼️ Fork

If you want to contribute to someone else's code, you would "fork" it. This creates a copy of the code under your account that you can make changes to. Create a fork when you want to make changes to someone else's code and contribute to it.

‼️ Branch

Creating a branch on a repository is like forking a repository. You would do this when you want to make changes to your code without harming a working version.

‼️ Pull Request

Once you make changes on a forked repository or another branch, you might need to bring the changes into the "main" repository. This allows YOUR changes to be visible in the main project! *You are basically asking for permission to "merge" your changes."

This allows you to:

⭐ Collaborate on code

⭐ Make comments

⭐ Review the contributions made

‼️ Command Line Interface

A Command Line Interface (CLI) is your computer's visual application for accessing its operating system. There are different types of CLIs for different operating systems, such as Terminal for MacOs and PowerShell for Windows. If you have Windows, make sure to also install Git Bash for a better tool. In upcoming issues, we will refer to your CLI as your Terminal or Command Line, but remember that they mean the same thing!

Key functions you should be familiar with after this task include:

  • Committing changes
  • Forking a repository
  • Making a new branch
  • Making a pull request
❓ What do all the emojis mean? Glad that you asked!
Emoji Meaning
💡 Helpful tips
‼️ Important info
Question you may have
Features

📹 Walkthrough Video

Alt text

Week 2: Getting Emotional With the Face API

Week 2 Step 3 ⬤⬤⬤◯◯◯◯◯ | 🕐 Estimated completion: 10-30 minutes

Getting Emotional ~ With the Face API

Remember back in step 1 we used Postman to call the API? Well we are going to call the Face API with our Azure Function!

✅ Task:

Create an Azure function that can send a static image to the Face API that returns emotion data

  • 1: Add Face API Keys and endpoint into your Function App's application settings.
  • 2: Edit your function to make a request to the Face API using node-fetch and send back the emotion data in the body in JSON format body: {the_data}
  • 3: Place your function URL in a repository secret called EMOTIONAL_ENDPOINT and commit your code to emotionalgifs/index.js on the emotionalgifs branch.
  • 4: Create a pull request to merge emotionalgifs onto main, and only merge the pull request when the bot approves your changes!

🚧 Test your Work

Use Postman! Paste the function url and make a POST request. Remember to attach the file in Body! In the output box, you should get the output. Make sure you're using an image with a real face on it or else it won't work. Here's an example of an output I get with this image:

✅ Expected Output The output should be in JSON format like so:
{
  "result": [
    {
      "faceId": "a16f522d-0577-4e50-97cb-1feeef7aaf2e",
      "faceRectangle": {
        "top": 313,
        "left": 210,
        "width": 594,
        "height": 594
      },
      "faceAttributes": {
        "emotion": {
          "anger": 0,
          "contempt": 0,
          "disgust": 0,
          "fear": 0,
          "happiness": 1,
          "neutral": 0,
          "sadness": 0,
          "surprise": 0
        }
      }
    }
  ]
}

1. Define keys to authenticate the request

This function takes in one parameter, img, that contains the image we're trying to analyze. Inside, we have two variables involved in the call: subscriptionKey and uriBase.

The process.env object allows you to access super-secret values in your backend. This prevents hackers from getting your keys and doing bad stuff (like exploiting your resources).

async function analyzeImage(img){
    const subscriptionKey = process.env.SUBSCRIPTIONKEY;
    const uriBase = process.env.ENDPOINT + '/face/v1.0/detect';
}

Follow this Documentation to save your keys in your Function App's application settings.

💡 When you put the key and endpoint in Azure's application settings, it will only work after it is deployed and running with the Azure function url. In order to use postman and test this on your computer before deploying to Azure, you will need to code the subscriptionKey and uriBase like this:

const subscriptionKey = "YOUR_SUBSCRIPTIONKEY"
const uriBase = "YOUR_LOCALHOST_URL"

💡 Afterwards, when commiting the code to github for the counselorbot to check, you will need to replace the hardcoded subscriptionKey and uriBase variables with process.env.SUBSCRIPTIONKEY and process.env.ENDPOINT

❓ Why do we need to do this?

When running your program on your own computer, the program has no way of accessing Microsoft Azure's function application settings, so process.env will not work. However, once you deploy the function onto Azure, the function now can "see" the application settings, and can use them through process.env

2: Call the FACE API

Create a new function analyzeImage(img) outside of module.exports that will handle analyzing the image. Keep in mind this function is async because we will be using the await keyword with the API call.

Get your Face API keys ready! We need to let our Face API know that we are authenticated to access this resource.

❓ What is an async function?

Javascript is what we call a "synchronous" language, meaning operations in Javascript block other operations from executing until they are complete, creating a sense of single directional flow. This means that only one operation can happen at a time. However, in order to maximize efficiency (save time and resources), Javascript allows the use of asynchronous functions.

Async functions allow other operations to continue running as they are being executed. Refer to this blog for more information.

Promises are sychnronous objects, similar to their real life meaning, return a value at some point in the future, or a reason for why that value could not be returned - they represent the result of an async function that may or may not be resolved.

Is JavaScript Synchronous or Asynchronous? What the Heck is a Promise?

Master the JavaScript Interview: What is a Promise?


❓ How do you specify the parameters for your request!

In order to specify all of our parameters easily, we're going to create a new URLSearchParams object. Here's the object declared for you. I've also already specified one parameter, returnFaceId, as true to provide an example. Add in a new parameter that requests emotion.

Remember, these parameters are coming from the Face API documentation!

Screen Shot 2021-05-16 at 9 20 19 PM
let params = new URLSearchParams({
	'returnFaceId': 'true',
	'<PARAMETER NAME>': '<PARAMETER VALUE>'     //FILL IN THIS LINE
})

🛠️ Calling the API

In the same way we installed parse-multipart, install node-fetch.
Read the API section of the documentation. We're going to make a call using the fetch(url, {options}) function.

To call the fetch function - use the await keyword, which we need because fetch returns a Promise, which is a proxy for a value that isn't currently known. You can read about Javascript promises here.

API Documentation can be tricky sometimes...Here's something to help

💡 Request Headers tell the receiving end of the request what type of data is in the body.

❓ How do I specify Request Headers?

Go back to the Face API documentation here, and find the Request headers section.

There are two headers that you need. I've provided the format below. Enter in the two header names and their two corresponding values.

FYI: The Content-Type header should be set to'application/octet-stream'. This specifies a binary file.

    //COMPLETE THE CODE
    let resp = await fetch(uriBase + '?' + params.toString(), {
        method: '<METHOD>',  //WHAT TYPE OF REQUEST?
        body: '<BODY>',  //WHAT ARE WE SENDING TO THE API?
      
      	//ADD YOUR TWO HEADERS HERE
        headers: {
            '<HEADER NAME>': '<HEADER VALUE>'
        }
    })
❓ What is the URL?

Notice that the URL is just the uriBase with the params we specified earlier appended on.

const uriBase = '<YOUR ENDPOINT>' + '/face/v1.0/detect';

❓ Still Confused?

Fill in the method and body.

async function analyzeImage(img){
    
    const subscriptionKey = '<YOUR SUBSCRIPTION KEY>';
    const uriBase = '<YOUR ENDPOINT>' + '/face/v1.0/detect';
    let params = new URLSearchParams({
        'returnFaceId': 'true',
        'returnFaceAttributes': 'emotion'
    })
    //COMPLETE THE CODE
    let resp = await fetch(uriBase + '?' + params.toString(), {
        method: '<METHOD>',  //WHAT TYPE OF REQUEST?
        body: '<BODY>',  //WHAT ARE WE SENDING TO THE API?
        headers: {
            '<HEADER NAME>': '<HEADER VALUE>'  //do this in the next section
        }
    })
    let data = await resp.json();
    
    return data; 
}

⏬ Receiving Data

Call the analyzeImage function in module.exports. Add the code below into module.exports.

Remember that parts represents the parsed multipart form data. It is an array of parts, each one described by a filename, a type and a data. Since we only sent one file, it is stored in index 0, and we want the data property to access the binary file– hence parts[0].data. Then in the HTTP response of our Azure function, we store the result of the API call.

//module.exports function
//analyze the image
const result = await analyzeImage(parts[0].data);
context.res = {
	body: {
		result
	}
};
console.log(result)
context.done(); 

📹 Walkthrough Video

walkthrough video

What is your deepest secret? The API

Week 3 Step 6 ⬤⬤⬤⬤⬤⬤◯◯◯ | 🕐 Estimated completion: 10-20 minutes

Can you keep a secret?

This week, you will be going through steps to set up a Twilio account and create an Azure function that texts your Twilio number.

✅ Task:

  • 1: Create a new branch named deepsecrets
  • 2: Create an HTTP Trigger Azure Function and add its URL to a repository secret named DEEPSECRETS_ENDPOINT
  • 3: Fill in the Azure Function URL as a webhook
  • 4: Code the Function so that it returns the message it was sent
  • 5: Test your function by texting your Twilio number and seeing output texted back to yourself.
  • 6: Commit your function code to deepsecrets/index.jsto the deepsecrets branch
  • 7: Create a pull request that merges deepsecrets to main, and only merge the pull request when the bot approves your changes!

🚧 Test Your Work

To test your work, try texting a message to your Twilio number - you should receive a text message back that repeats your own message.

❓ How do I find my Twilio number?

See your phone numbers.

❓ Example output?

194569969_1687986154736022_4227041675617722938_n

Note: You might not get a text back on your phone because Twilio sometimes won't allow you to return a single variable as the entire body, but as long as the code passes the test, you will be okay!

💡 Yay! This means you've successfully configured your Twilio webhook.

Set Up a Twilio Account

Since you should already have a trial Twilio Account from Week 2, you will only need to reconfigure the webhook URL. You will use the same phone number as before.

1: Create an Azure Function

Next, we'll want to create an Azure Function that will eventually output the content of texts that your Twilio number receives. This will just be a simple HTTP trigger function.

Finally, we need to install the npm package qs to use in our function code later.

2: Configure Your Webhook URL

When someone sends a text message to your Twilio number, Twillio can call a webhook: your Azure Function. You can send a reply back simply by returning your message in the request body.

❓ Webhook? Twilio? I'm lost!

Fear not!

Webhooks are essentially just HTTP callbacks that are triggered by an event - in our case, this event is receiving an SMS message. When that event occurs, Twilio makes an HTTP request to the URL configured for the webhook.

We'll configure the Webhook URL by filling in the Azure Function URL as a webhook.

❓ How do I find my Azure Function URL?

Navigate to your Function page (Overview), and click Get Function URL.

image

❓ How do I configure my Twilio webhook URL?
  1. Go to the Twilio Console's Numbers page

image

  1. Click on the phone number you'd like to modify

  2. Scroll down to the Messaging section and the "A MESSAGE COMES IN" option.

  3. Paste in your Azure Function URL. Make sure to click Save afterwards!!

image

3. Write your Azure Function

Now, we'll want to write our Azure Function. Go to the code for the function you created previously in this step.

First, we'll need to instantiate our qs npm package in order to use it.

❓ How do I use the `qs` package?
const querystring = require('qs');

Next, we'll use the querystring object to parse our request's body.

❓ How do I parse the request body?
const queryObject = querystring.parse(req.body);

From this output, we'll find its Body attribute and return it.

❓ How do I return the output?
context.res = {
   body: queryObject.Body
};

Since we are returning queryObject.Body as the entire body, you might not receive a text back on your phone. This is Twilio's issue, but the counselor bot's test should pass fine if your code is correct still!

📹 Walkthrough Video

walkthrough video

Week 3: Upload it!

Week 3 Step 2 ⬤⬤◯◯◯◯◯◯◯ | 🕐 Estimated completion: 10-20 minutes

Upload it!

This week, you will be going through steps to upload images to blob storage using Azure's SDK.

✅ Task:

  • 1: Receive an image from a POST request using parse-multipart
  • 2: Upload an image using @azure/blob SDK by naming the image test + the correct file extension
  • 3: Store your function url to your repository secrets with the name BUNNIMAGE_ENDPOINT, add your blob url to your repository secrets with the name blob_url.
  • 4: Commit your updated function code to bunnimage/index.js on the bunnimage branch
  • 5: Create a pull request and only merge the pull request when the bot approves your changes!

🚧 Test Your Work

To test your work, you'll be using Postman to send a POST request in Postman with an image in the body to your function url. You should see a response similar to the below:

{
  "body" : "File Saved"
}

💡 Yay! This means it was successfully saved.

❓ How do I attach an image to my POST request?
  1. Get your bunnimage function url

  2. Use Postman to make a POST request to your functional url

    image

  3. You will need to send body data with your request:

    • The Body tab in Postman allows you to specify the data you need to send with a request
    • You can send various different types of body data to suit your API
    • Website forms often send data to APIs as multipart/form-data
    • You can replicate this in Postman using the form-data Body tab
    • Be sure to check File instead of Text, since we'll be posting an image instead of a JSON object

    image

❓ How do you check your blob storage container to see if the image is stored there?

https://user-images.githubusercontent.com/69332964/99189316-9c592980-272e-11eb-9870-dbc1f9352599.png



Writing our First Azure Function to Upload an Image

❓ How do I initialize packages in code?
  1. Use this tutorial to add in your own connection string from your storage container
    • The storage container is the one you created in step 1
    • Navigate to the container and find your connection string
  2. Add the following lines of code to the top of your index.js file:
    const multipart = require("parse-multipart")
    const connectionString = process.env.AZURE_STORAGE_CONNECTION_STRING;
    const { BlobServiceClient } = require("@azure/storage-blob");
    • Take note of the process.env value being assigned to connectionString. AZURE_STORAGE_CONNECTION_STRING is the name of the environment variable.



❓ How do I find my secret strings?

These are the same ones you added in your repository secrets in step 1. Here is a review:

https://user-images.githubusercontent.com/69332964/99161822-ec4ed680-26c3-11eb-8977-f12beb496c24.png

  • Note: You'll need to store these strings in environment variables as well, if you don't want to accidentally commit them. You can access these with process.env['thesecretname']

1: Reviewing parse-multipart to receive an image

In your main module.exports function, you'll want to use the parse-multipart library to parse the image from the POST request. Then you'll determine the fle extension, and then upload the file using an uploadFile() function we'll write later on.

❓ Can we review syntax for `parse-multipart`?

To parse a request's body, you can use the following lines of code:

const boundary = multipart.getBoundary(req.headers['content-type']);
const body = req.body;
const parsedBody = multipart.Parse(body, boundary);

2: Uploading the image

Let's start out by writing the function we can call to upload an iamge.

Uploading the image blob to your container

Our uploadFile function will be an asynchronous function that uses the BlobServiceClient to get a reference to the container, create a blob, and upload the data to that blob.

❓ What should my parameters be?

The signature of your uploadFile() function should look something like:

async function uploadFile(parsedBody, ext)
❓ How can I get a reference to the container?
const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString);
const containerName = "<YOUR_CONTAINER_NAME>";
const containerClient = blobServiceClient.getContainerClient(containerName);    // Get a reference to a container
❓ How can I create a blob?
const blobName = 'test.' + ext;    // Create the container
const blockBlobClient = containerClient.getBlockBlobClient(blobName); // Get a block blob client

Based on previous code we've written and logic, fill in the blanks!

❓ How can I upload data to the blob?
const uploadBlobResponse = await blockBlobClient.upload(parsedBody[0].data, parsedBody[0].data.length);

💡 Be sure to return a string like "File Saved" from this function when the file has been uploaded!

Heading back to the module.exports main function

❗ Name your image file as test.png or test.jpg (depending on the submitted file extension) in our code for testing purposes.

❓ How can I determine file extension?

You can use a series of if-else statements like the ones below:

let filetype = parsedBody[0].type;
if (filetype == "image/png") {
    ext = "png";
} else if (filetype == "image/jpeg") {
    ext = "jpeg";
} else if (filetype == "image/jpg") {
    ext = "jpg"
} else {
    username = "invalidimage"
    ext = "";
}
❓ How can I upload the file?

In this case, we'll just call the uploadFile() function that we wrote earlier.

let responseMessage = await uploadFile(parsedBody, ext);
context.res = {
    body: responseMessage
};

4: Add your Blob URL as a secret

You'll need to add your Blob URL to the github repository as a secret so we can test it! Name our secret to blob_url and set it equal to your blob url, which should look like "https://bunnimagestorage.blob.core.windows.net". To find your url, simply place your storage account name in this template:

https://<your account name>.blob.core.windows.net

📹 Walkthrough Video

walkthrough video

Week 2: Ok, Boomer :neutral_face:

Week 2 Step 8 ⬤⬤⬤⬤⬤⬤⬤⬤ | 🕐 Estimated completion: 5-10 minutes

Ok, Boomer 😐

✅ Task:

Modify your Azure Function so that it texts the user back with a song.

  • 1: Retrieve song url from a JSON object
    const songs = {"GenZ":"https://open.spotify.com/track/0SIAFU49FFHwR3QnT5Jx0k?si=1c12067c9f2b4fbf", 
    "GenY":"https://open.spotify.com/track/1Je1IMUlBXcx1Fz0WE7oPT?si=a04bbdf6ec4948b9", 
    "GenX":"https://open.spotify.com/track/4Zau4QvgyxWiWQ5KQrwL43?si=790d9e3ef2ed408d", 
    "BabyBoomers":"https://open.spotify.com/track/4gphxUgq0JSFv2BCLhNDiE?si=1abb329f2dc24f50", 
    "Unknown":"https://open.spotify.com/track/5ygDXis42ncn6kYG14lEVG?si=84b49b41d09d4d11"}
  • 2: Use ${} to quickly insert strings
  • 3: Return the url with a personalized message in the body of the request formatted like
We guessed you're part of this generation: [insert generation]! Happy listening! [song link]`
  • 4: Commit your updated code to song4u/index.js in the song4u branch, and only merge the pull request when the bot approves your changes!

🚧 Test Your Work

To test your work, try texting a jpg image to your Twilio number (with a face!). You should receive a text back that contains the required message format.

Example:

Sent from your Twilio trial account - We guessed you're part of this generation: GenZ! Happy listening! https://open.spotify.com/track/0SIAFU49FFHwR3QnT5Jx0k?si=1c12067c9f2b4fbf

Working with JSON

JSON has keys and values. In our case, the generation is the key, and the url is the value.

See if you can determine the url with this syntax:

let value = json_object[key]

Shortcuts with Strings

You might be used to concatenating strings and variables like this:

let string = "Hello " + name

That can get tiring, though. Try this syntax:

let string = `Hello ${name}`

📹 Walkthrough Video

walkthrough video

Continuing your work

Final Project Step 7 ⬤◯◯◯◯◯ | 🕐 Estimated completion: 30 mins

Continuing your work...

✅ Task:

  • Continue working in the final-project branch
  • Review all of the issues that are currently open:
    • Close the issues that you have completed
    • Add 3-4 more issues based on the work you plan on doing this week (if they are not already there)
    • Label the issues you want to work on as "Priority"
    • Label the issues that you need your mentor's help with as "mentor" and send them to your mentor to review before your meeting
  • 🚀 Commit any changes to the final-project branch, make a pull request, and merge it

Week 1: Name your Cat

Week 1 Step 7 ⬤⬤⬤⬤⬤⬤⬤◯◯ | 🕐 Estimated completion: 5-15 minutes

Name your Cat

✅ Task:

  • Run git pull
  • 1: Modify your Azure Function to output TWO generated cat pictures from the cat API and TWO random names from list below:
  • 2: Return the images, encoded in base64, and names in JSON format in the body
    body: {
        cat1: your-first-catpicture-in-base64,
        cat2: your-second-catpicture-in-base64,
        names: [name1, name2]
    }
  • 3: Commit your updated function code in twocatz/index.js to the twocatz branch, create a pull request, and only merge the PR when the bot approves your changes!

🚨 NOTE: If the CataaS API is not working, please use this alternate endpoint: https://bit-cat.azurewebsites.net/cat/says/serverless

🚧 Test your Work

When you paste your Function URL in your browser or make a GET request with Postman, you might get something like:

{
  "cat1": "/9j/4AAQSk...",
  "cat2": "R0lGODlhCwHI...",
   "names": [
    "Daniel",
    "Shreya"
  ]

1: Select random items out of a list

  1. Create an array with the names first
  2. Generate a random number within the range of the array length
❓ How do I generate two random names?
  1. Create an array with the names:
let names = ["name1", "name2"...]
  1. Generate a random value in the correct range:
let random_value = Math.floor(names.length * Math.random())
  1. Get the name!
let resultname = names[random_value]
  1. Wrap the code for generating a random combination into a function that returns resultname and call the function twice to get two names!



2: Return images in JSON format

context.res is the key to answering this question!

❓ How do I return the images using context.res?

To return your two images and two names in the output:

context.res = {
    body: {
        cat1: your-first-catpicture-in-base64,
        cat2: your-second-catpicture-in-base64,
        names: [name1, name2]
    }
}

📹 Walkthrough Video

walkthrough video

Laying out the groundwork

Final Project Step 2 ⬤⬤◯◯◯◯ | 🕐 Estimated completion: 30 minutes

Laying out the groundwork

✅ Task:

  • Make sure you're on the final-project branch
  • Create a flowchart that demonstrates how different components in your project interact with each other
    • Create a new file on figma.com or whimsical.com called "Serverless Camp Project"
    • Include any Azure Functions and APIs you plan on using
  • 🚀 Upload the flowchart as a jpg/png file in the "project" folder
  • 🚀 Commit your changes to the final-project branch, make a pull request, and merge it

Mapping it out

To get started with building, you will first need to create a flowchart showing the flow of information and components you will include to make there project work. Nothing has to be finalized, but planning it out allows you to organize your ideas and thought processes.

Purpose of Programming Flowcharts

  • Plan before the development phase - this helps you save a lot of hours of work
  • Document your project and communicate how it works to others (this is really valuable when someone else is helping you debug your code)
  • Prevent major technical issues with your code before you event start
  • Ensure that you are more effectively using all the technologies available to you
  • You will need to refer back to the flowchart while developing to review your work and sometimes come up with new ideas

Converting the checklist into a flowchart

  1. Create a shape for each of the components from your checklist and label them inside their designated shape
  2. Draw lines on all of the technologies that interact with each other
  3. Near each line, use the previous bullet points you wrote to describe the relationship between the technologies starting with action verbs (e.g. between JS code/front-end --> Azure Function "Sends an HTTP Post Request with uploaded image)
  4. Use arrow heads to show the direction in which one technology is impacting another

Week 3: Codename Blob + 3RR0R Handling

Week 3 Step 3 ⬤⬤⬤◯◯◯◯◯◯ | 🕐 Estimated completion: 10-20 minutes

Codename Blob + 3RR0R Handling

This week, you will be going through steps to handle POST requests with no data.

✅ Task:

  • 1: Modify your function code so that it receives a header value called codename and uses it to name the image.
    • If an image isn't attached, return "Sorry! No image attached."
    • If an image is attached, return the output of the uploadFile() function
  • 2: Commit your updated function code to bunnimage/index.js to the bunnimage branch.
  • 3: Create a pull request that merges bunnimage onto main, and only merge the pull request when the bot approves your changes!

🚧 Test Your Work

To test your work, use Postman to send a POST request without an image attached. You should see a response similar to the below:

{
  "body" : "Sorry! No image attached."
}

💡 Yay! This means the error was successfully caught.

1: Modify the Function

Handle Empty Data

Now we'll want to handle cases where the POST request's body happens to be empty. In your original module.exports function, before you parse the body, make sure it exists! Only then should you upload your file to blob storage.

❓ How do I catch empty POST requests?

Use an try-catch statement to catch when parse-multipart is unable to parse the empty body. If catches an error, set the responseMessage to "Sorry! No image attached." Otherwise, you can safely parse the body!

let responseMessage = ""
try {
    let password = // get the header called "codename"
    // use parse-multipart to parse the body
    // determine the file-type here!
    responseMessage = await uploadFile((place your parsedBody here), (place the extension here), (place the "codename" here));
    // fill the parameters in!
} catch(err) {
    context.log("Undefined body image");
    responseMessage = "Sorry! No image attached."
}

💡 Hint: responseMessage is what we're returning to the user as the output.

💡 Hint: You'll need to add another parameter to the uploadFile function since it now has to receive the body, extension, AND filename!

Headers

Headers are yet another way to pass on information in HTTP requests. Here's how to access them:

let the_header_value = req.headers['insert_header_name'];

In this case, the header value will be used to name our picture.

📹 Walkthrough Video

walkthrough video

Week 2: Getting Emotional Returning the Dominant Emotion

Week 2 Step 4 ⬤⬤⬤⬤◯◯◯◯ | 🕐 Estimated completion: 5-10 minutes

Getting Emotional ~ Returning the Dominant Emotion

✅ Task:

Modify your Azure Function so that it returns the Dominant Emotion of an Image.

  • 1. In your function emotionalgifs, determine the dominant emotion in the emotion data and output the dominant emotion in the request body when you make a POST request
  • 🚀 Commit your code to emotionalgifs/index.js on the emotionalgifs branch
  • Create a pull request to merge emotionalgifs onto main, and only merge the pull request when the bot approves your changes!

1: Finding the Dominant Emotion

In order to match the results of the Face API with Gifs from the Giphy API, we need to determine the dominant emotion from the API response.

🛠️ Modifying the Azure Function

We need to access the emotion data by itself, without the face id and other analyzed data. To do this, we need to create another variable in the module.exports async function.

let emotions = result[0].faceAttributes.emotion;

💡 Now you've got the JSON object with all the emotion values, find the highest valued emotion! Use context.log(emotions) to see how it's structured.

We're accessing the data at index 0 because we're analyzing one face. If there were two faces, the data for the second face would be stored at index 1.

❓ How do I find the max value from the JSON object?

Recall that the entire JSON response object looks like this:

{
  "result": [
    {
      "faceId": "a16f522d-0577-4e50-97cb-1feeef7aaf2e",
      "faceRectangle": {
        "top": 313,
        "left": 210,
        "width": 594,
        "height": 594
      },
      "faceAttributes": {
        "emotion": {
          "anger": 0,
          "contempt": 0,
          "disgust": 0,
          "fear": 0,
          "happiness": 1,
          "neutral": 0,
          "sadness": 0,
          "surprise": 0
        }
      }
    }
  ]
}

💡 The code that we want is the emotion part of the response, not the entire thing.

  • Therefore, we will use the previously created variable emotions, which stores:
"emotion": {
          "anger": 0,
          "contempt": 0,
          "disgust": 0,
          "fear": 0,
          "happiness": 1,
          "neutral": 0,
          "sadness": 0,
          "surprise": 0
}

💡 In JSON, the key values are what you use to access the value. {key: value}, or in our case, {emotion: value}. If we wanted to access happiness from the emotions JSON, we can use emotions["happiness"], which returns 1.

In this example, we see that happiness has the highest value. Here are the steps to do this in code:

1️⃣ We need to create an array with the emotion values (ranging from 0 to 1) so that we can manipulate it and find the dominant emotion. Object.values() converts an object's values into an array, with each value in the object stored as a separate element:

let objects = Object.values(WHAT_IS_YOUR_JSON);
// FILL IT IN
// What your array could look like: [0.01, 0.34, .....]

In our example, writing Object.values(emotions) would return an array of [0, 0, 0, 0, 1, 0, 0, 0].

💡 In JSON, the keys are what you use to access the value. {key: value}, or in our case, {emotion: value}.

  • However, if each value in a JSON object is unique to a key (meaning that if no 2 keys have the same value), we can also use the value to find the corrosponding key. Now, we need to find the dominant emotion in the array objects:

2️⃣ Let's break this line down.

const main_emotion = Object.keys(emotions).find(key => emotions[key] === Math.max(...objects));
  • Math.max(...objects) finds the max value of an array. Let's say it's 1. Here is more info on ... in JavaScript.
  • Object.keys(emotions).find(key => emotions[key] === Math.max(...objects)); finds the emotion, or key, that matches the max value of 0.99. Let's say it's happiness.
    • Similar to Object.values, Object.keys will return an array of the keys in an object, [anger, contempt, etc]
    • The .find method on an array returns the value of the first element in the provided array that satisfies the provided test.
    • By looking at each key, and checking if the corrosponding value in the JSON is the max value emotion, we are able to see which emotion has the highest value (and is thereby the dominant emotion)

3️⃣ Now, main_emotion contains the dominant emotion! All we need to do is output main_emotion when the function is called:

context.res = {
        // status: 200, /* Defaults to 200 */
        body: main_emo_return
};

📹 Walkthrough Video

walkthrough video

Week 4: Error Handling Sir this is a Wendys

Week 4 Step 2 ⬤⬤◯◯◯◯◯ | 🕐 Estimated completion: 20-25 minutes

Error Handling ~ Sir this is a Wendy's

Demo: 🐰

Ideally, users will fill the web form with the correct information. However, people make mistakes. In this step, we will be implementing some form validation to make sure that a user has inputted text value before submitting their password.

✅ Task:

  • 1: Check you are on the bunnimage-frontend branch
  • 2: Change your HTML form to accept multipart/form-data in index.html
  • 3: Add a file input to index.html that accepts image/x-png,image/jpeg and add the attribute name=image
  • 4: Modify the function in index.js to create an alert if either the file input or the name input is null
  • 5: Modify the button so that it submits the form by changing its type attribute to "submit"
  • 6: Prevent the page from reloading after submission
  • 7: Commit your updated code to bunnimage/index.html and bunnimage/script.js
  • 8: Create a pull request to merge bunnimage onto main, and only merge the pull request when the bot approves your changes!

🚧 Test your Work

Open up your LiveServer plugin and start your server.

‼️ You have three test cases to try
  1. The "correct" way: Submit both a file and text. Check that you receive "Thanks!"
  2. The "unexpected" way (file): Submit a file that is not png or jpeg. Does it work?
  3. The "unexpected" way (text input): Try submitting without entering a username. You should get an alert box that says "No name error."


1: Accepting Images as an Input

In HTML Forms, the enctype attribute specifies how the form-data should be encoded when submitting it to the server. Like we learned before, if we want to upload files, we need send form-data encoded as multipart/form-data

    <h1>Bunnimage</h1>
    <form enctype="multipart/form-data">
    <label>Code: </label>

To add the image upload input, add an additional file input within the form & change the submit button's type.

<form enctype="multipart/form-data">
   <input type="file" name="image"></input>
   <input id="username" type="text" placeholder="Enter your file's name">
   <input type="submit" />

2: Input Validation?

We need to validate two things.

❓ How do you check that the image is either .png or .jpeg format

The HTML <input> accept Attribute specifies a filter for what file types the user can pick from the file input dialog box. The accept attribute can only be used with .

<form enctype="multipart/form-data">
    <input type="file" accept="image/x-png,image/jpeg" name="image"></input>
    <input id="username" type="text" placeholder="Enter your file's name">
    <input type="submit" />

Additionally, the HTML may not catch an invalid file 100% of the time, so we will also need to use JavaScript to do some validation.

  1. Once a file is uploaded, you will need to display an error when a file of invalid format is added.
  2. Get the name of the file from the input box (use the change event listener for the input)
  3. Check if the file is either .png or .jpeg using this StackOverflow post



❓ How do you check that the text box isn't empty

To validate that the name isn't null, check if document.getElementById("username").value isn't empty, to change the output div to "Thanks!", or display an alert("No name error.")

💡 Hint: Use your JavaScript conditional skills!


📹 Walkthrough Video

walkthrough video

Documentation

Final Project Step 6 ⬤⬤⬤⬤⬤⬤ | 🕐 Estimated completion: 25 minutes

Documentation

✅ Task:

  • Document your first component in blog.md
  • 🚀 Commit your changes to the final-project branch, make a pull request, and merge it

Once you have successfully completed developing your first component, you can start documenting your project blog. Remember that the purpose of this blog is to help someone else create your project from scratch, so as you start writing, you will need to break down your code as much as possible and explain it so that someone else can learn from your example.

Check out the Save Money and Frustration on Amazon using Azure Functions blog to see how Fifi broke down her project into multiple steps.

Screen Shot 2021-06-05 at 1 03 19 PM

Check out the Creating a File Sharing and Conversion Web App with Azure Functions blog to see how Emily breaks down her code into smaller chunks to document, describes how it works, and pastes the code at the bottom.

Screen Shot 2021-06-05 at 1 00 40 PM

Quick tips

  • If your project required any kind of groundwork like installing npm packages or setting up Azure locally, document this first in "Step 0"
  • Break down your code:
    • Copy all of your code and paste it into a code snippet in your blog
    • Break it down into multiple snippets in order of how you wrote the code
    • Before each snippet, write a description of what the code is for and how it works
    • Define keywords that were new to you or would be to your audience
    • Tip: use substeps to make your blog easier to follow

If you have completed multiple components of your project this week, document them as you go.

Documentation Best Practices:

❗ How do I document my code?

Documentation is easier than it seems! Here are some tips to consider when you begin:

  1. Write for your audience. In this case, you're writing an educational blog meant to educate students who have little-no coding experience. Consider where you were at when you started this curriculum and make sure someone at that same level would be able to make sense of your blog.
  2. Talk directly to your audience (as we are here) and help guide them over the course of your blog to develop this really cool project.
  3. If there are concepts/terms that seem complicated, try your best to explain them in a way that would make sense to you.
  4. Be intentional about what you choose to explain. Some technical concepts require understanding other concepts as well, but you don't want your blog to become a dictionary. Choose the concepts that matter to your project the most, and add external links to documentations/websites for those that you don't want to get into but would be valuable for your audience to understand.
  5. Make it fun to read! Think about how Buzzfeed articles are organized into steps and have a lot of gifs/emojis/images/other visuals to help engage the audience.



Start Coding!

Final Project Step 5 ⬤⬤⬤⬤⬤◯ | 🕐 Estimated completion: 1-5 hours

Start Coding!

✅ Task:

  • Based on your schedule determined in "timeline.md", find the first issue you need to work on and begin your first task
  • 🚀 Close the issue when you have completed
  • 🚀 Commit your changes to the final-project branch, make a pull request, and merge it

Developing the first component

💡 Start small then build big!

Let's look back at the advice we got from the previous step: "start small then build big." Your goal is to get something working - not to developing your final project in 1 week.

  • Begin by just having one working component of the project. Ex:
    • Press a button on your webpage and receive information
    • Create a working Azure Function that can return information
  • After that's complete and tested, begin building onto that with more and more features!

Committing code

❗ Best practices for commiting code is to commit it constantly. Any time you make a change, commit the change.

❓ Why?

Every time you develop something that works, commit and push it immediately. This is so that if/when your code breaks due to a bug, you can easily revert back to the latest working commit. It also shows a history of how you have worked on your code.

💡 Tip:: You want to name the commit accurately to the changes you have made so that they are easy to search through later.

Week 2: How old are you??

Week 2 Step 7 ⬤⬤⬤⬤⬤⬤⬤◯ | 🕐 Estimated completion: 15-25 minutes

How old are you??

✅ Task:

Modify your Azure Function so that it determines the user's generation.

  • 1: Write a function to download the image from the link you parsed in the previous step.
  • 2: Use node-fetch to create a POST request to the Face API to receive an age.
  • 3: Use vanilla Javascript to determine the generation from the age returned from the Face API using the following:
Generation Range
GenZ 5 < age < 25
GenY 24 < age < 41
GenX 40 < age < 57
BabyBoomers 56 < age < 76
Unknown Any other age
  • 4: Commit your updated code to song4u/index.js on the song4u branch
  • 5: Create a pull request to merge song4u onto main, and only merge the pull request when the bot approves your changes!

🚧 Test Your Work

To test your work, try texting a jpg image to your Twilio number (with a face!). After you text the image, you can context.log(age) or context.log(generation) to verify that your program and the FaceAPI is working!

We use context.log and not console.log, because Azure uses the context object in Functions.

‼️ Twilio does not allow a simple string to be sent. For example, the following code will NOT send a text back, but will still pass the bot's test, and allow you to move on to the next step.

context.res = {
        body: generation
    };
// THIS CODE WILL NOT RETURN ANYTHING BY TWILIO, but will work on Github!

Downloading image data

Based on the previous step, you should now be able to access your image url. We were able to access it with queryObject.MediaUrl0.

❓ How do I download the image?

Perform a quick GET request with fetch.

💡 Remember that you need to initialize variables for your packages!

    let resp = await fetch(YOUR_URL,{
        /*The await expression causes async function execution to pause until a Promise is settled 
        (that is, fulfilled or rejected), and to resume execution of the async function after fulfillment. 
        When resumed, the value of the await expression is that of the fulfilled Promise*/
        method: 'GET',
    })

    // receive the response
    let data = await resp.arrayBuffer()
    // we are receiving it as a Buffer since this is binary data

Analyzing the image

Recall the analyzeImage() function we wrote in the previous project. This time, you will be calling for age data instead of emotion data.

💡 Don't forget to read the documentation!

❓ What's the syntax again?

⭐ Retrieve age data from the Face API.

async function analyzeImage(img){
    const subscriptionKey = process.env['subscriptionkey'];
    const uriBase = // WHAT'S YOUR ENDPOINT?;
	// env variables (similar to .gitignore/.env file) to not expose personal info

    let params = new URLSearchParams({
	'returnFaceId': 'true',
	'returnFaceAttributes': //WHAT GOES HERE?
    })

    // making the post request
    let resp = await fetch(uriBase + '?' + params.toString(),{
        method: 'POST',
        body: img,
        // img is the parameter inputted
        headers: {
            'Content-Type' : 'application/octet-stream',
            // HOW DO YOU AUTHENTICATE?
        }
    })

    // receive the response
    let data = await resp.json();

    return data;
}

However, this code won't work. Fill in the code where needed using your previous project and the documentation.

❓ How do I get an age from my analysis function?

Like you've done before, call the analyzeImage() function with your image you downloaded.

💡 Tip: Always context.log() your output so it's easier to determine how to access object attributes.

The function returns face data formatted in JSON. We can determine the age like so:

let age = result[0].faceAttributes.age

This retrieves the first face, the faceAttributes attribute, and the age attribute from the previous object.

Determining and returning Generation

Using the provided names and age ranges, you can use if else logic in Javascript to determine the generation.

❓ Can I get an example?
if (age > 5 && age < 25) {
    id = "GenZ"
}

id is the variable we will return as the final generation.

&& means "and". The age needs to be greater than/equal to 5 AND less than/equal to 20 for the id = "GenZ to run.

Remember, Twilio will not allow a simple text like GenZ to go through, so all you have to do is return the generation in the body! We will return more data in the next steps, so you will be able to get a text back.

📹 Walkthrough Video

walkthrough video

Week 1: Let me in!

Week 1 Step 5 ⬤⬤⬤⬤⬤◯◯◯◯ | 🕐 Estimated completion: 25-35 minutes

Let me in!

✅ Tasks:

  • Run git pull origin [BRANCH_NAME]
  • 1: Add on to the HTTP Trigger in the previous issue to check if the user’s parameter input of “password” equals letmein.
    • If it does, output "Access granted."
    • If it doesn’t equal the correct password, output “Access denied.”
  • 2: Commit the update function's code in a file named hackervoice/index.js to the root of the hackervoice branch!
  • 3: Create a pull request to merges hackervoice to main, but do not merge unless the bot approves your changes.

🚧 Test your Work

When you paste your Function URL in your browser or make a GET request with Postman with a password parameter of "letmein", you should get a response of “Access granted.“. Otherwise, your function should output “Access denied.“.

‼️ Make sure you follow the exact responses we provided. Access denied. and Access granted.

1: Return from a Function

In the previous step, we received the password (the user's input). Now, we need to return either Access denied. or Access granted. to the user based on their input. We can do this by returning it in the body of the request!

💡 Recall the context.res object we saw in the HTTP Trigger template.

context.res = {
    // status: 200, /* Defaults to 200 */
    body: your_response
};

Place your message to the user in your_response!

📹 Walkthrough Video

walkthrough video

Week 2: Getting Emotional With Parse-Multipart

Week 2 Step 2 ⬤⬤◯◯◯◯◯◯ | 🕐 Estimated completion: 5-20 minutes

Getting Emotional ~ With Parse-Multipart

Create an Azure function that takes in an image from a HTTP request, parses it with parse-multipart, and returns the base64 code of the image

✅ Task:

  • Create a new branch called emotionalgifs
  • 1: Create a new Azure Function called emotionalgifs with the HTTP Trigger template, install and use parse-multipart to parse an image from an HTTP request, and save its raw data in a variable
  • 2: Return the image data encoded in base64 in the body of the Azure Function response
  • 3: Place your function URL in a repository secret called EMOTIONAL_ENDPOINT and commit your code to emotionalgifs/index.js on your emotionalgifs branch
  • 4: Create a pull request that merges emotionalgifs to main, and only merge the pull request when the bot approves your changes!

🚧 Test your Work

Use Postman! Paste the function url and make a POST request. Remember to attach the file in Body! In the output box, you should get the output. Make sure you're using an image with a real face on it or else it won't work. Here's an example of an output I get with this image:

✅ Expected Output

The output should be the base64 code of the inputted image, like this:

/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQIC...
❓ Confused about Postman?
  1. Navigate back to the Postman app and change GET to POST
  2. Publish/deploy your function and copy your function url from the VS Code output like this:
  3. Use the function url and any image you want to send the POST request. Remember to attach the file in Body, and send it using form-data!

💡 Keep in mind

  • Note that when adding a file to form-data, you do NOT need to specify a key, and can send only a value (which in our case is a file).

  • To change the value to a file, hover over the value box, click on the Text dropdown and select File.

    Untitled_ Nov 11, 2020 6_40 PM


1. Create emotionalgifs HTTP trigger function that parses images

What is multipart request?

A HTTP multipart request is a HTTP request that HTTP clients construct to send files and data over to a HTTP Server.

💡 This is commonly used by browsers and HTTP clients to upload files to the server.

Because we want to send an image file through an HTTP Request, we need a piece of software to parse the raw data to extract the image file. Here comes the handy NPM package: parse-multipart!

The raw payload formatted as multipart/form-data will looks like this (expand)
------WebKitFormBoundaryDtbT5UpPj83kllfw
Content-Disposition: form-data; name="uploads[]"; filename="somebinary.dat"
Content-Type: application/octet-stream

some binary data...maybe the bits of a image..
------WebKitFormBoundaryDtbT5UpPj83kllfw
Content-Disposition: form-data; name="uploads[]"; filename="sometext.txt"
Content-Type: text/plain

hello how are you
------WebKitFormBoundaryDtbT5UpPj83kllfw--

The lines above represents a raw multipart/form-data payload sent by some HTTP client via form submission containing two files. We need to extract all the files contained inside it. The multipart format allows you to send more than one file in the same payload, so that's why it is called multipart.

❓ Installing `parse-multipart`

Before you can install parse-multipart, you need to enter npm init -y into the terminal. This command allows us to set up a new npm package:

Screen Shot 2021-07-12 at 1 42 15 PM

Open up a terminal in VSCode inside your function's directory, type npm install parse-multipart, and press enter.

💡 Forgot how to navigate a terminal? Check this out.

Note: the text outputted by the console does not mean there was an error! The npm package has successfully been installed.

❓ How do I use this package?

First, we need to declare the variable multipart outside of the async function so that we can access the NPM package:

const multipart = require('parse-multipart');

Notice that multipart.Parse(body, boundary) requires two arguments, as it has two parameters. I've already gotten the boundary for you – just like the documentation example, our boundary is a string in the format "----WebKitFormBoundary(random characters here)".

In the multipart.Parse() call, you need to figure out what the body parameter should be.

💡 Hint: It should be the request body. Think about the template HTTP Trigger Azure function. How did we access the body in there?

// here's your boundary:
const boundary = multipart.getBoundary(req.headers['content-type']);
  
// TODO: assign the body variable the correct value
const body = '<WHAT GOES HERE?>'

// parse the body
const parts = multipart.Parse(body, boundary);

2. Receiving the image

When the POST request is made to this function with an image, we need to:

  1. Get the image from the parts (parse-multipart) variable
  2. Convert the image to base64
  3. Store the converted image in the response body
❓ What parts of the template code do we need?

Take a look at the standard module.exports function code:

module.exports = async function (context, req) {
    // the code
}

This is the function that runs every time your HTTP trigger gets a request. As a parameter of this function, the req parameter contains all the information the request was sent with. This contains:

  • Headers
  • The body

Remove all of the content in module.exports except this:

context.res = {
        // status: 200, /* Defaults to 200 */

        body: //LEAVE THIS BLANK
};



❓ How do we output the image in base64?

Next, we want to output the base64 code of the inputted image. The parsed image data that we need to convert to base64 is is stored in index 0 of parts since we only sent one file, and we want the data property of this image to access the binary file. Thus, we will be converting parts[0].data to base64 and assigning the code to a new variable:

let convertedResult = Buffer.from(parts[0].data).toString('_____');
// FILL IN THE BLANK

The Buffer part of the code provides temporary storage for the binary image data as it is converted to base64.

Now, complete the following so that the base64 code is outputted when the function is called:

context.res = {
        // status: 200, /* Defaults to 200 */

        body: //WHAT GOES HERE?
};



📹 Walkthrough Video

walkthrough video

Week 3: Your times up....

Week 3 Step 4 ⬤⬤⬤⬤◯◯◯◯◯ | 🕐 Estimated completion: 10-20 minutes

Your time's up....

This week, you will be going through steps to create a Timer Trigger Function delete all files in your Blob storage container every 5 minutes. This assists us in automated cleanups for our storage container and removing outdated files that users no longer need.

✅ Task:

  • 1: Create a Timer Trigger Function to delete all files in your Blob storage container every 5 minutes.
  • 2: Test your new Timer Trigger Function
  • 3: Commit your new code to the bunnimage branch!
  • 4: Comment an expression that tells the Timer Trigger to run everyday at 6:23 AM.

💡 Tip: Cron expressions might help you with the last task.

🚧 Test Your Work

To test your work, use Postman to send a POST request with an image to your previous HTTP trigger function that will save your file within your blob. Recall that a successful save of your file will cause you to see a response similar to the below:

{
  "body" : "File Saved"
}

You should also double check that your file has been saved by navigating to your storage blobs.

Now run your timer trigger function using func start and re-check your storage blobs - they should be gone. Your logs notifying you of the blobs' deletions should also be displayed in your trigger function's console.

💡 Yay! This means your timer trigger function worked.


1: Create your Timer Trigger Function

First, we'll need to create a new Azure Function that runs on a schedule with the Timer Trigger template.

  1. Create a new Function, naming it bunnimage-timer and select Timer trigger

image

  1. Leave the Schedule as 0 */5 * * * * to have the function be triggered every 5 minutes.

image

  1. Press enter on your keyboard.

2: Code your Timer Trigger Function

  • Reference your container string from your environment secrets
  • Reference the @azure/storage-blob package
  • Reference your account name
❓ How do I create the global variables above?

To reference the @azure/storage-blob package:

const { BlobServiceClient } = require("@azure/storage-blob");

To reference your connection string and account name:

const connectionstring = process.env["AZURE_STORAGE_CONNECTION_STRING"];
const account = "YOUR_STORAGE_ACCOUNT_NAME";

To delete all the blobs in the container, you can first list all the blobs, then loop through deleting each one as you go.

❓ How do I delete a blob?

Use the deleteBlob method from the Azure SDK! Here's an example:

await blobContainerClient.deleteBlob(filename)
❓ How do I call the `deleteBlob` function within `module.exports` and loop through existing blobs?

Exactly like the beginning of your deleteBlob function, you'll want to:

  1. Create a BlobServiceClient object using your connection string.
  2. Create a variable that references the name of the container that contains the file you want to delete.
  3. Fetch the container with that name.
const blobServiceClient = await BlobServiceClient.fromConnectionString(connectionstring);
const deletecontainer = "images";
const blobContainerClient = await blobServiceClient.getContainerClient(deletecontainer);

Now you'll want to use the listBlobsFlat function to retrieve a reference to an enumeration of your blobs. Loop through these blobs, and delete each one.

for await (const blob of blobContainerClient.listBlobsFlat()) {
    context.log(`Deleting blob name ${blob.name}`);
    
    // a line of code here should access the blob's name and use `deleteBlob()` to delete the blob!
}

You can also add a log after your for loop that notifies you that all the blobs have been deleted.

context.log("Just deleted your blobs!")

📹 Walkthrough Video

walkthrough video

Week 3: Leaky secrets

Week 3 Step 7 ⬤⬤⬤⬤⬤⬤⬤◯◯ | 🕐 Estimated completion: 20-30 minutes

Leaky secrets

Store a message from Twilio in CosmosDB and return the most recent message

✅ Task:

  • 1: Configure CosmosDB and add your CosmosDB Endpoint and Key in Azure App settings as COSMOS_ENDPOINT and COSMOS_KEY
  • 2: Write functions to store a message and retrieve the most recent entry
  • 3: Take a message from a Twilio POST request's parameters and use your functions to return the most recent message
  • 4: Commit your updated function code to deepsecrets/index.jsto the deepsecrets branch
  • 4: Create a pull request to merge deepsecrets onto main, and only merge the pull request when the bot approves your changes!

❗ Make sure to return your message in this format:

Thanks 😊! Stored your secret "insert_user's_message". 😯 Someone confessed that: "the_most_recent_message"

🚧 Test Your Work

To test your work, send your Twilio number a text message. Once you do so, if you navigate to your Cosmos database account, go to Data Explorer, and the dropdowns underneath your new SecretStorer database, you should see an item that contains a message with your text message!

image

Important Note: If you are triggering the function for the first time, you may see an error message like the one below:
TypeError: Cannot read property 'message' of undefinedStack: TypeError: Cannot read property 'message' of undefinedat module.exports (C:\home\site....)

Don't worry! Try sending another text message, and everything should work fine the second time around. This error occurs because you don't have anything stored in CosmosDB yet.

💡 Yay! This means you've successfully connected Twilio with your Azure Cosmos database and Function.


1: Create your Cosmos Databases

Before we do anything else, we'll want to create our Cosmos DB account.

❓ How do I create my Cosmos DB account?
  1. Navigate to your Azure Portal and click on Azure Cosmos DB under Azure Services.

image

  1. Click Create Azure Cosmos DB Account.

image

  1. Under Select API Option, choose Core (SQL) - Recommended.

image

  1. Fill in your account name, leave default options as they are, and click Review + create.

image

  1. Click Create a final time.

image

  1. Once deployment is complete, click Go to resource.

image

  1. Before you return to VSCODE, record the Cosmos Database URI and Key. That should be under Setting/Keys
    Screenshot from 2021-07-08 21-05-47-edited_cosmo_key

  2. Create new Azure function application settings per Azure App Setting. Label them COSMOS_ENDPOINT with the cosmos URI as the value and COSMOS_KEY with the cosmos Key as the value.
    Screenshot from 2021-07-08 14-45-27-edited_function_app

1. Install @azure/cosmos

Now that your Azure Cosmos DB account is created, we should install the npm package @azure/cosmos.

🔧 1. Configuration

First, we need to instantiate a variable CosmosClient from the @azure/cosmos package we just downloaded.

const CosmosClient = require("@azure/cosmos").CosmosClient;

Create a config object that contains all of the sensitive information that we need to manipulate our data.

Call it config, and place keys for endpoint, key, databaseId, containerId, and partitionKey.

❓ What should my config object look like?

Here is an example of the config object. Make sure your databaseId, containerId, and partitionKey are correct.

const config = {
  endpoint: process.env.COSMOS_ENDPOINT,
  key: process.env.COSMOS_KEY,
  databaseId: "SecretStorer",
  containerId: "secrets",
  partitionKey: {kind: "Hash", paths: ["/secrets"]}
};

2. The create Function

Now, we want to write an asynchronous create function that takes in the parameter of client (this will be our CosmosClient), databaseId, and containerId.

The create function will:

⭐️ Use the client to create a database with an id of databaseId if it does not exist.

⭐️ Use the client to create a container inside the database of ID databaseId. This container will have an ID of containerId and a key of partitionKey.

⭐️ The create function should be async because within it we are accessing CosmosDB.

⭐️ The create function does not need to have a return value.

❓ How do we do this?

Read Microsoft's documentation on how to create a database and container.
You will need to find which part of the docs to use, because in the real-world, you aren't always given the picture perfect instructions. You will need to figure out what code you need.

Hint: You don't need to create a new file like the docs say, because we already have our index.js file.

You also do not need the module.exports, because this function will be in our index.js file, and that is where we will be using it.

💡 Hint: The config.[value] variables are accessed from the config object you created earlier.

📜 2. The createDocument Function

⭐️ The createDocument async function will create a new document within the database container that contains the newItem data.

⭐️ newItem should be a parameter that is passed into the createDocument function.

⭐️ return your items object from the function which contains the most recent message.

  1. In the createDocument function, we first need to establish a connection with your Azure Cosmos account. This way, your code can tell the database what it wants, and the database will respond!
  2. In order to create this connection, read these docs

Note: remember when we created the create function? We need to use that function now! You will need to slightly modify one line of code in the docs to work with yoru create function!

  1. After creating a connection with the CosmosDB, we need to query the database for the most recent document (so you can see someone else's secret 👀)

Note: Instead of their SQL query, we need to use SELECT top 1 * FROM c order by c._ts desc to only get the most recent document!

  1. After stealing someone else's secret, it is only fair that we upload our own!
  2. We need to create a new item in the database!
  3. Return that secret that we stole ‼️‼️

3: The Main Function

Instead of directly passing queryObject.Body (the SMS message) to context.res:

⭐️ You'll instead save it in a JSON object with a key of message.

⭐️ Then, pass the JSON object containing the SMS message into the createDocument function you just coded to get your items object.

⭐️ Finally, create a custom response that you can send back to the original texter.

❓ How do I create a new document with queryObject.Body?
const queryObject = // use the `qs` npm package to parse the request body
let message = queryObject.Body;
let document = // create an object with the string `"message"` as the key, and the variable `message` as its value
let items = // call the createDocument function with the document we just made

Your response message must adhere to this format! Make sure to change the variable names message and items if you named them something different.

const responseMessage = `Thanks 😊! Stored your secret "${message}". 😯 Someone confessed that: ${JSON.stringify(items[0].message)}`

📹 Walkthrough Video

walkthrough video

Week 4: Error Handling Sir this is a Wendys

Week 4 Step 2 ⬤⬤◯◯◯◯◯ | 🕐 Estimated completion: 20-25 minutes

Error Handling ~ Sir this is a Wendy's

Demo: 🐰

Ideally, users will fill the web form with the correct information. However, people make mistakes. In this step, we will be implementing some form validation to make sure that a user has inputted text value before submitting their password.

✅ Task:

  • 1: Check you are on the bunnimage-frontend branch
  • 2: Change your HTML form to accept multipart/form-data in index.html
  • 3: Add a file input to index.html that accepts image/x-png,image/jpeg and add the attribute name=image
  • 4: Modify the function in index.js to create an alert if either the file input or the name input is null
  • 5: Modify the button so that it submits the form by changing its type attribute to "submit"
  • 6: Prevent the page from reloading after submission
  • 7: Commit your updated code to bunnimage/index.html and bunnimage/script.js
  • 8: Create a pull request to merge bunnimage onto main, and only merge the pull request when the bot approves your changes!

🚧 Test your Work

Open up your LiveServer plugin and start your server.

‼️ You have three test cases to try
  1. The "correct" way: Submit both a file and text. Check that you receive "Thanks!"
  2. The "unexpected" way (file): Submit a file that is not png or jpeg. Does it work?
  3. The "unexpected" way (text input): Try submitting without entering a username. You should get an alert box that says "No name error."


1: Accepting Images as an Input

In HTML Forms, the enctype attribute specifies how the form-data should be encoded when submitting it to the server. Like we learned before, if we want to upload files, we need send form-data encoded as multipart/form-data

    <h1>Bunnimage</h1>
    <form enctype="multipart/form-data">
    <label>Code: </label>

To add the image upload input, add an additional file input within the form & change the submit button's type.

<form enctype="multipart/form-data">
   <input type="file" name="image"></input>
   <input id="username" type="text" placeholder="Enter your file's name">
   <input type="submit" />

2: Input Validation?

We need to validate two things.

❓ How do you check that the image is either .png or .jpeg format

The HTML <input> accept Attribute specifies a filter for what file types the user can pick from the file input dialog box. The accept attribute can only be used with .

<form enctype="multipart/form-data">
    <input type="file" accept="image/x-png,image/jpeg" name="image"></input>
    <input id="username" type="text" placeholder="Enter your file's name">
    <input type="submit" />

Additionally, the HTML may not catch an invalid file 100% of the time, so we will also need to use JavaScript to do some validation.

  1. Once a file is uploaded, you will need to display an error when a file of invalid format is added.
  2. Get the name of the file from the input box (use the change event listener for the input)
  3. Check if the file is either .png or .jpeg using this StackOverflow post



❓ How do you check that the text box isn't empty

To validate that the name isn't null, check if document.getElementById("username").value isn't empty, to change the output div to "Thanks!", or display an alert("No name error.")

💡 Hint: Use your JavaScript conditional skills!


📹 Walkthrough Video

walkthrough video

[TOP SECRET] Please open

Week 1 Step 8 ⬤⬤⬤⬤⬤⬤⬤⬤◯ | 🕐 Estimated completion: 5-15 minutes

[TOP SECRET] Morse Code Converter

Dwight being a spy

Welcome agent! You have made it this far so we know we can trust you. BitProject is working in an undercover operation, and we need a new way to communicate.

✅ Tasks:

  • Run git pull and create a new branch called morse
  • 1: Install the morse-code-converter package
  • 2: Code an HTTP trigger function named morse with the npm package morse-code-converter. This HTTP trigger takes in a plaintext parameter (which is in english) returns a response of morse code.
  • 3: Place the function URL of your new Morse Code HTTP Trigger as a repository secret named MORSE_ENDPOINT and commit your function's code to a file named morse/index.js to the morse branch
  • 4: Create a pull request that merges morse to main, and only merge the PR when the bot approves your changes

1: Installing Packages

Create a new HTTP trigger function using the Azure extension in VSCode, and choose the Function App you previously created during this week.

We will be using the morse-code-converter npm package.

❓ Why do we need these "packages" and what are they?

Packages are awesome! They're chunks of publicly available code that someone else has written to help make coding easier for everyone else. These packages reusable code that increases functionality in your code.

Before the Azure Function can run the code we will write, we have to install all the necessary package dependencies. These packages contain code that we will "depend on" in the application; we have to install them in the console using npm install.

💡 What is a package?

💡 What is the morse-code-converter package?



❓ How do I install the package?

In VS Code, open your terminal.

💡 On Windows or Mac, go to the header of your window, and go to Terminal --> New Terminal.
image

Enter these commands in order:

npm init -y 

npm install morse-code-converter



‼️ Woah! What just happened when the package was installed?

The first command created a package.json file to store your dependencies and essentially keeps track of what packages your application needs. You can find this file by going into the left menu and clicking on "App Files".

Screen Shot 2021-04-26 at 3 15 21 AM

The next one actually installs the necessary packages with code, morse-code-converter.

💡 Note: If you get red text like WARN, you can ignore it.

Screen Shot 2021-04-26 at 3 12 43 AM



‼️ Reminder: don't forget to import your package! `const morse = require("morse-code-converter");

💡 Make sure your parameter is named plaintext

2: Using morse-code-converter

❓ How do I receive the English as a parameter?

Query parameters can be accessed from the req object in the input of the module.exports function.

💡 Since ours is named plaintext, we can access it with req.query.plaintext.

How would I send the English?
[place your function url here]&plaintext=[insert the English]


❓ How do I use the Morse Code package?

Tip: Try reading the documentation first.

  1. First require the npm package at the top of your code.

    const morse = require("morse-code-converter");
  2. Query the url for the parameter plaintext, and store it in a variable.

  3. Create a variable named code, but set it to undefined for right now, because we are not sure if plaintext contains a value or not (we can't translate nothing 🤔)

  4. To check if the user passed in nothing for plaintext, we need to use an if-else conditional.

  5. We first check if the user did not pass in a parameter of plaintext at all, or if plaintext has no value. In which case, we will tell them to enter some text.

    if (typeof plaintext === 'undefined' || plaintext === "") {
        code = "Please enter some text to  convert!"
    }

    💡 the || means or in JavaScript. Either the left side can be true, or the right side can be true, and the code inside the if will run!

  6. Now that we checked if the user has entered nothing, we can add code that will execute when the user has entered something for plaintext. Add the code below after the entire if statement (after the opening and closing brackets).

    else {
        code = morse.textToMorse(plaintext);
    }
  7. Now, we just need to respond to the HTTP request with code! The final if-else code should look like:

    if (typeof plaintext === 'undefined' || plaintext === "") {
        code = "Please enter some text to convert!"
    }
    else {
        code = morse.textToMorse(plaintext);
    }



💡 Be sure to return the code in the body of the response!

❓ Um. Body?

Tip: context.res is the object you use to return a response to the user.

    context.res = {
        body: [insert your encoded English here]
    };



Don't forget to git pull before making any changes to your local repo!!

‼️ Remember: we test your Morse Code function everytime you commit!

📹 Walkthrough Video

walkthrough video

Open up!

Week 1 Step 4 ⬤⬤⬤⬤◯◯◯◯◯ | 🕐 Estimated completion: 35-45 minutes

Building our first function ⚡[HackerVoice]

Managing a server is pretty complicated. As a student, mastering serverless functions can help you to build projects that solve real-world problems by integrating APIs, constructing user interfaces, and analysing data without worrying about managing servers.

⚡️ 10 Ways to Use Serverless Functions

✅ Tasks:

  • Run git pull and create a new branch called hackervoice
  • 1: Create an Azure account
  • 2: Set up Azure locally on VS Code
  • 3: Create an HTTP trigger Azure function that gets content from a request parameter called password and returns it in the function's body
  • 4: Test your function locally with Postman and, when you confirm that it works, deploy your function
  • 5: Place your function URL from the Azure portal in a repository secret called HACKERVOICE_ENDPOINT and commit the function's code in a file named hackervoice/index.js on the hackervoice branch
  • 6: Create a pull request that merges hackervoice to main, and only merge when the bot approves your changes!**

🚧 Test your Work

Option 1:
Paste the function url directly in your browser and add the query parameters to the end of the url: &param_name=param_value. Your inputted password value should appear.

Option 2:
Use Postman! Paste the function url and make a GET request. In the output box, you should receive the value you put in for the request parameter.

1: Create an Azure Account

Azure is Microsoft's cloud computing platform (similar to Google Cloud Platform and Amazon Web Services).

  1. To create an Azure account, go to Microsoft Azure and press Start Free.
  2. After signing in with your Microsoft account and filling in your personal details, you will be asked to add a credit card.
  • This is only for security purposes (preventing multiple free accounts per person).
  • You won't be charged unless you choose to buy a premium account, which we do not need for this course.

2: Set Up Azure Locally on VS Code

  1. Confirm if you have installed the Azure Tools extension on VS Code
  2. Create your local project
  • ‼️ When prompted to set the language, choose JAVASCRIPT not C#)
  • ‼️ When prompted for the Authorization level, always choose Function
  1. Run the function locally
  2. Publish the project to Azure
  3. Get your function url the Azure Portal
  4. Test the function on Postman
❓ What should running the function look like?

Start the debugger by going to index.js and then pressing the F5 key.

Having trouble with Azure Tools? Try installing it this way



Once you receive the localhost link in the terminal, follow it and notice the terminal log "Executing 'Functions.[Name of your function]'" indicating that you made a request to the function.

local



If the request is successfully made in Postman this is what it should look like:

Postman

Once you deploy/publish the code to Azure successfully, you will get the function url in the Output of VS Code:

output

3: Create your own HTTP Trigger Function

You should see the HTTP Trigger Function template code Microsoft Azure starts you with when you first create your trigger.

❓ What is happening in index.js?
  • Start of function definition: module.exports = async function
  • Print comment in Azure Console anytime the function is triggered: context.log()
  • Get parameter from request body: const name
  • Conditional (ternary) operator to print message if parameter exists (else print error message):
    //condition: if name exists
    name
    //? is chosen if the condition evaluates to true
    ? "Hello, " + name + ". This HTTP triggered function executed successfully."
    //: is chosen if the condition evaluates to false
    : "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.";
  • Results of that conditional ternary statement are assigned to responseMessage which is returned in the function body

Now, let's begin coding by modifying that template. First, you will need to retrieve content from a request parameter named password. Recall that parameters look like this in a full URL:

www.mywebsite.com/api/endpoint?param1=hi&param2=hi
❓ How to get content from a request parameter?

Request parameters are a way for an HTTP request to take in information! They are pretty much identical in purpose to why you would want a parameter for a function in a coding language. In this instance, we will need a parameter for the password.

  • Request parameters are a property of the req or request parameter of the module
  • The request has a query object that stores all of the parameters passed in
  • You don't need to specify what parameters the user needs to input into the HTTP request
  • You can acess any parameters that are sent in

You would access a parameter by calling on the query like this:

<property name> = req.query.<your property here>;

//example:
let color = req.query.color;

💡 If the user makes a request with a parameter of <url>?color=blue then the variable color in your function will hold that value.

💡 Note: your parameter must be named password

Finally, we have to return something to the users. In this case, we will be returning the value of the request parameter we just retrieved.

❓ How can I return something in the function body?

In Azure, context is an object that holds data about the response of the HTTP function. Read more about Accessing the request and response.

In our HTTP trigger function, we have defined context object with the body property:

context.res = {
        // status: 200, /* Defaults to 200 */
        body: responseMessage
    };

To return the password in body, simply replace responseMessage with password.

4: Test your Function with Postman

  1. Deploy your function to Azure
  2. Get your function url from the Output window on VS Code(same as before) and make a request on Postman
  3. Add a parameter with the key password and give it any random value
  4. Make sure that your function is returning the password parameter in the body

5: Add Repository Secrets

❓ How do I add a respository secret?

Here are some steps:

  1. On GitHub, navigate to the main page of the repository.
  2. Under your repository name, click Settings.
    settings
  3. In the left sidebar, click Secrets.
  4. Click New repository secret.
  5. Type a name for your secret in the Name input box.
  6. Enter the value for your secret.
  7. Click Add secret.


📹 Walkthrough Video

walkthrough video

Week 3: Download it!

Week 3 Step 5 ⬤⬤⬤⬤⬤◯◯◯◯ | 🕐 Estimated completion: 10-20 minutes

Download it!

This week, you will be going through steps to create an HTTP Trigger that takes parameter inputs and returns a download link for the given image.

✅ Task:

  • 1: Create an HTTP Trigger Function named bunnimage-download that takes in "username" from the header
    • Have the function query for image with the correct associated username from blob storage using node-fetch
    • Have the function return an image download link in an attribute named downloadUri.
  • 2: Place your new HTTP Trigger function secret in the repository named BUNNIMAGE_ENDPOINT2
  • 3: Commit your new HTTP Trigger function code in bunnimage-download/index.js on the bunnimage branch
  • 4: Create a pull request to merge bunnimage onto main, and only merge the pull request when the bot approves your changes!

🚧 Test Your Work

To test your work, use Postman to send a GET request with a "username" in the header. You should see a response similar to the below:

💡 The username value should be the name of an image you have already uploaded to the blob storage. Do it fast, or your timer trigger will delete it!

{
  "body" : {
     "downloadUri" : "https://<YOUR_STORAGE_ACCOUNT_NAME>.blob.core.windows.net/images/<USERNAME>.png",
     "success": true
  }
}

💡 Yay! This means you successfully fetched a download link to your image.

❓ How do I add "username" to the header?

Click on the headers tab below the request URL.
image


1: Modifying your Function

In our module.exports function, we'll want to:

  1. Use the node-fetch package we installed earlier this week to determine if the requested image is available.

  2. Use if-else statements to determine the success of the retrieval. Remember to test for both jpeg and png extentions!

  3. Send back a JSON object containing the download link.

❓ How do I use fetch to test extensions?

Outside of the main function, you'll first want to create a variable called fetch that calls your node-fetch package: const fetch = require("node-fetch");.

Then, within your main function, you'll need to create references to your username as well as variables for download, downloadpng, and downloadjpg. Since we don't know whether the image is a png or jpeg file, we need to test for both.

const username = req.headers['username'];
let download = ""
let downloadpng = "https://<YOUR_BLOB_STORAGE_URL>.blob.core.windows.net/images/" + username + ".png";
let downloadjpg = "https://<YOUR_BLOB_STORAGE_URL>.blob.core.windows.net/images/" + username + ".jpeg";

To attempt to download the image, call fetch asynchronously. This way, we can test all possible links to the image and determine which one works.

let pngresp = await fetch(downloadpng, {
   method: 'GET',
})
let pngdata = await pngresp;

let jpgresp = await fetch(downloadjpg, {
   method: 'GET',
})
let jpgdata = await jpgresp;
❓ How do I determine the right image link?

Your data will contain an attribute "status text" that lets you know if a blob doesn't exist. To use these to our advantage, we can create if-else statements that notify us if our fetch method was successful.

if (pngdata.statusText == "The specified blob does not exist." && jpgdata.statusText == "The specified blob does not exist." ) {
   success = false;
   context.log("Does not exist: " + pngdata)
   context.log("Does not exist: " + jpgdata)
} else if (pngdata.statusText != "The specified blob does not exist.") {
   success = true;
   download = downloadpng
   context.log("Does exist: " + pngdata)
} else if (jpgdata.statusText != "The specified blob does not exist.") {
   success = true;
   download = downloadjpg
   context.log("Does exist: " + jpgdata)
}
❓ How do I return the download link?

To return the download link, just set context.res to a JSON object with your download link.

context.res = {
      body: {
               "downloadUri" : download,
               "success": success,
      }
};
context.log(download);
context.done();

📹 Walkthrough Video

walkthrough video

[TOP SECRET] Please open

Week 1 Step 8 ⬤⬤⬤⬤⬤⬤⬤⬤◯ | 🕐 Estimated completion: 5-15 minutes

[TOP SECRET] Morse Code Converter

Dwight being a spy

Welcome agent! You have made it this far so we know we can trust you. BitProject is working in an undercover operation, and we need a new way to communicate.

✅ Tasks:

  • Run git pull and create a new branch called morse
  • 1: Install the morse-code-converter package
  • 2: Code an HTTP trigger function named morse with the npm package morse-code-converter. This HTTP trigger takes in a plaintext parameter (which is in english) returns a response of morse code.
  • 3: Place the function URL of your new Morse Code HTTP Trigger as a repository secret named MORSE_ENDPOINT and commit your function's code to a file named morse/index.js to the morse branch
  • 4: Create a pull request that merges morse to main, and only merge the PR when the bot approves your changes

1: Installing Packages

Create a new HTTP trigger function using the Azure extension in VSCode, and choose the Function App you previously created during this week.

We will be using the morse-code-converter npm package.

❓ Why do we need these "packages" and what are they?

Packages are awesome! They're chunks of publicly available code that someone else has written to help make coding easier for everyone else. These packages reusable code that increases functionality in your code.

Before the Azure Function can run the code we will write, we have to install all the necessary package dependencies. These packages contain code that we will "depend on" in the application; we have to install them in the console using npm install.

💡 What is a package?

💡 What is the morse-code-converter package?



❓ How do I install the package?

In VS Code, open your terminal.

💡 On Windows or Mac, go to the header of your window, and go to Terminal --> New Terminal.
image

Enter these commands in order:

npm init -y 

npm install morse-code-converter



‼️ Woah! What just happened when the package was installed?

The first command created a package.json file to store your dependencies and essentially keeps track of what packages your application needs. You can find this file by going into the left menu and clicking on "App Files".

Screen Shot 2021-04-26 at 3 15 21 AM

The next one actually installs the necessary packages with code, morse-code-converter.

💡 Note: If you get red text like WARN, you can ignore it.

Screen Shot 2021-04-26 at 3 12 43 AM



‼️ Reminder: don't forget to import your package! `const morse = require("morse-code-converter");

💡 Make sure your parameter is named plaintext

2: Using morse-code-converter

❓ How do I receive the English as a parameter?

Query parameters can be accessed from the req object in the input of the module.exports function.

💡 Since ours is named plaintext, we can access it with req.query.plaintext.

How would I send the English?
[place your function url here]&plaintext=[insert the English]


❓ How do I use the Morse Code package?

Tip: Try reading the documentation first.

  1. First require the npm package at the top of your code.

    const morse = require("morse-code-converter");
  2. Query the url for the parameter plaintext, and store it in a variable.

  3. Create a variable named code, but set it to undefined for right now, because we are not sure if plaintext contains a value or not (we can't translate nothing 🤔)

  4. To check if the user passed in nothing for plaintext, we need to use an if-else conditional.

  5. We first check if the user did not pass in a parameter of plaintext at all, or if plaintext has no value. In which case, we will tell them to enter some text.

    if (typeof plaintext === 'undefined' || plaintext === "") {
        code = "Please enter some text to  convert!"
    }

    💡 the || means or in JavaScript. Either the left side can be true, or the right side can be true, and the code inside the if will run!

  6. Now that we checked if the user has entered nothing, we can add code that will execute when the user has entered something for plaintext. Add the code below after the entire if statement (after the opening and closing brackets).

    else {
        code = morse.textToMorse(plaintext);
    }
  7. Now, we just need to respond to the HTTP request with code! The final if-else code should look like:

    if (typeof plaintext === 'undefined' || plaintext === "") {
        code = "Please enter some text to convert!"
    }
    else {
        code = morse.textToMorse(plaintext);
    }



💡 Be sure to return the code in the body of the response!

❓ Um. Body?

Tip: context.res is the object you use to return a response to the user.

    context.res = {
        body: [insert your encoded English here]
    };



Don't forget to git pull before making any changes to your local repo!!

‼️ Remember: we test your Morse Code function everytime you commit!

📹 Walkthrough Video

walkthrough video

Week 2: Getting Emotional With the Face API

Week 2 Step 1 ⬤◯◯◯◯◯◯◯ | 🕐 Estimated completion: 5-20 minutes

Getting Emotional ~ With the Face API

Because of amazing APIs, you don't need to be an expert in machine learning and AI to take advantage of cutting edge technology. In this project, we are going to be building an API and webpage to return you a GIF when you upload a picture of yourself!

✅ Task:

Create a request in Postman to send an image of a person to the Azure Face API to return the subject's emotions

  • 1: Create a Face API Endpoint
  • 2: Create a POST request in Postman to the Face API to retrieve the emotion attributes of this image
  • 2: Place the API endpoint and subscription key in the GitHub repository secrets: API_ENDPOINT AND SUBSCRIPTION_KEY
  • 3: Comment the output of your Postman request to move on

🚧 Test your Work

You should get the following expected output if you have configured your Face API correcty, as well as sent the request with the correct parameters and body.

✅ Expected Output
{
  "result": [
    {
      "faceId": "d25465d6-0c38-4417-8466-cabdd908e756",
      "faceRectangle": {
        "top": 313,
        "left": 210,
        "width": 594,
        "height": 594
      },
      "faceAttributes": {
        "emotion": {
          "anger": 0,
          "contempt": 0,
          "disgust": 0,
          "fear": 0,
          "happiness": 1,
          "neutral": 0,
          "sadness": 0,
          "surprise": 0
        }
      }
    }
  ]
}

1: The Face API

The Face API will accept the image and return information about the face, specifically emotions. Watch this video on Microsoft Cognitive Services for an in-depth explanation: http://www.youtube.com/watch?v=2aA8OEZ1wk8

❓ How do I create and access the Face API?
  1. Log into your Azure portal
  2. Navigate to Create a Resource, the AI + Machine Learning tab on the left, and finally select Face and fill out the necessary information
  3. Record and save the API endpoint and subscription key
  4. Place the API endpoint and subscrition key in the GitHub repository secrets: API_ENDPOINT AND SUBSCRIPTION_KEY
    • These keys will be used in the Azure function to give access to this API

❓ Where can I find the Face API keys?
  1. Navigate to the home page on the Micrsoft Azure portal (https://portal.azure.com/#home)
Screen Shot 2021-02-04 at 4 00 33 PM
  1. Click on the resource you need the keys for
Screen Shot 2021-02-04 at 4 00 49 PM
  1. On the left menu bar, locate the Resource Management section and click on "Keys and Endpoint"
Screen Shot 2021-02-04 at 12 26 36 PM

2: Using Postman to Send a Request

Now, we can test if our API is working using Postman. Make sure to pay close attention to the documentation and the API Reference

Request URL

Request URL is used when a web client makes a request to a server for a resource. Notice that the request url listed in the API reference is this:

https://{endpoint}/face/v1.0/detect[?returnFaceId]\[&returnFaceLandmarks]\[&returnFaceAttributes]\[&recognitionModel]\[&returnRecognitionModel][&detectionModel]

Parameters

Parameters are typically used in requests to APIs to specify settings or customize what YOU want to receive.

❓ What are the parameters for the request?

The Request URL has the following parameters in [ ]:

  • [?returnFaceId]
  • [&returnFaceLandmarks]
  • [&returnFaceAttributes]
  • [&recognitionModel]
  • [&returnRecognitionModel]
  • [&detectionModel]

Important things to note:

  • All of the bracketed sections represent possible request parameters
    • Read through Request Parameters section carefully
    • How can we specify that we want to get the emotion data?
  • All of the parameters are optional
    • We can delete the parameters we don't need in our request
    • Your request URL only requres one parameter, with a specific value
  • Between detect and your parameter, add ?
  • If you had more than one parameter, you would need to place & between each (but not between detect and your first parameter)
    • Since we only have one parameter, no & are needed

💡 All of this is located in the documentation! Find this section to read more:
image

Request Headers

Request Headers tell the receiving end of the request what type of data is in the body.

❓ How do I specify Request Headers?
  • Go back to the Face API documentation here, and find the Request headers section.
    • The Content-Type header should be set toapplication/octet-stream. This specifies a binary file.
    • The Ocp-Apim-Subscription-Key header should be set to one of your two keys from your Face API resource.
    • Request headers are not part of the request URL. They are specified in the Postman headers tab:
    Screen Shot 2021-05-27 at 6 33 07 PM

Request Body

The body of a POST request contains the data you are sending.

❓ How do I send the image in the body of the POST request?

To send a post request, click on the dropdown and select POST. This means that we are going to send data to the server. Prior to this, we have been getting data from the server with a GET request.
Screen Shot 2021-06-25 at 7 25 58 PM

Go to the body tab of your Postman request and select binary:
Screen Shot 2021-05-27 at 6 37 53 PM

Next, just upload the image and send your POST request.

📹 Walkthrough Video

walkthrough video

Week 4: Making fetch Happen

Week 4 Step 3 ⬤⬤⬤◯◯◯◯ | 🕐 Estimated completion: 20-25 minutes

Making fetch Happen

Demo: 🐰

We are going to how use the form to call our Azure Function! Yay 🎉! Things are coming together.

✅ Task:

  • 1: Modify the function and use await fetch to send the form input to your Bunnimage Azure Function
  • 2: If the upload was successful, change the output div to Your image has been stored successfully!
  • 3: Modify your CORS settings in your function app so your frontend can make requests.
  • 4: Commit your updated code to bunnimage/script.js to the bunnimage-frontend branch
  • 5: Create a pull request to merge bunnimage-frontend onto main, and only merge the pull request when the bot approves your changes!

🚧 Test your Work

Open up your LiveServer plugin and start your local server. To test your web app:

  1. Upload an image and click submit. Do you get a "Your image has been stored successfully!" message?
  2. Log into your Azure portal and navigate to your storage account. In your container, do you see your image?

1: How do I get form data?

To get the information the user entered in the form, you will need to create a FormData object. FormData lets you compile a set of key/value pairs to send.

const bunniForm = document.getElementById("YOUR_FORM_ID");
const payload = new FormData(bunniForm);
const file = fileInput.files[0]; // fileInput is the file upload input element
payload.append("file", file);
❓ What's happening here?

Code breakdown:

  • we reference the html form element with document.getElementById("YOUR_FORM_ID"), so we can create a FormData object.
  • we extract the file the user uploaded using fileInput.files[0]
  • finally, we add the file created to the FormData object using append, and a key-value pair. The key is file, and its value is the file itself.

💡 Since we need to reference the form element in document.getElementById("YOUR_FORM_ID"), we need to give it an id in the html.

<form enctype="multipart/form-data" id="YOUR_FORM_ID">

Now, we can send the payload to our azure function in the body of our fetch request.


2: How do I call the Azure Function?

The Fetch API is built in to Javascript to make HTTP requests (using GET, POST and other methods), download, and upload files.

💡 We don't have to install node-fetch as an npm package since fetch is built into JavaScript. Remember: we were using NodeJS before.

To start a request, call the special function fetch():

const response = await fetch(resource, options);

Fetch accepts two arguments:

  • resource: the URL string, or a Request object
  • options: the configuration object with properties like method, headers, body, credentials, and more.
❓ What does fetch exactly do?

fetch() starts a request and returns a promise. When the request completes, the promise is resolved with the Response object. If the request fails due to some network problems, the promise is rejected.

Keep this in mind if your function throws a promise error!


3: How to configure "options"

In order to pass the type of method, the payload in the pody, and headers, you have to configure the "options" part of the fetch function.

const response = await fetch(resource, {
   method: ... ,
   body: ... , 
   headers: { ... }
});

Fill in the method, headers and body with the correct information compatible with your API!

💡 Refer back to previous Fetch requests we made in NodeJS for hints. Remember for this request, we are not receiving JSON!

How to validate that the upload worked

We need to let the users know if the upload was successful! We do this by making sure we receive a response from our upload API:

❗ Use a "try...catch"

A try...catch is what it sounds like - it catches errors. This makes for a better user experience and is crucial to error handling.

try { 
    // Try receiving data from your fetch request
    // Change the output div's value
} catch (e) {
    // If an error occurred, tell the user!
}



4: Update CORS Settings

CORS (Cross-origin resource sharing) settings tells the Function App what domains (such as wwww.website.com) can make requests to the Azure Functions.

❓ How can I change it?

Head to your function app in your portal and find the button on the left hand side called CORS.
https://user-images.githubusercontent.com/69332964/99188905-6f0b7c00-272c-11eb-8142-f91882227c78.png


❓ What should I change it to?

Change it to a wildcard operator (*), which allows all origin domains to make requests.

💡 Be sure to remove any other existing inputs before attempting to save with wildcard. Don't forget to save your changes!


📹 Walkthrough Video

Part 1

walkthrough video

Part 2

walkthrough video

Week 1: Getting Cat Pics with Postman

Week 1 Step 3 ⬤⬤⬤◯◯◯◯◯◯ | 🕐 Estimated completion: 10-15 minutes

Getting Cat Pics with Postman 🐱

In this step, you will be using Postman Desktop Application to test the CataaS API by sending a GET request.

✅ Tasks:

  • 1: Read and explore the documentation for the Cataas API
  • 2: With Postman, specify the API Endpoint and set the parameters to send a GET request to the CataaS API and receive a cat picture with "Serverless" written on it
  • 3: Comment, but do not Close with comment, the picture you receive from the API in this issue to move on

🚨 NOTE: If the CataaS API is not working, please use this alternate endpoint: https://bit-cat.azurewebsites.net/cat/says/serverless

1: Exploring the CataaS API

CATAAS is a RESTful API that exclusively delivers images of cats. The main feature is that we can change the properties of images, add text, truncate the image, add a filter and more. It's not an API with many real-world applications, but it's perfect for learning.

⭐ Interested in playing around with the API? Documentation is here.

❓ How does the cloud work?

"The cloud" refers to servers that are accessed over the Internet, and the software and databases that run on those servers. Cloud servers are located in data centers all over the world. By using cloud computing, users and companies don't have to manage physical servers themselves or run software applications on their own machines.

❓ What are HTTP Requests?

A server is a computer that provides (serves) data to other computers, called clients. Clients connect to servers through the internet.

Clients communicate with servers with through HTTP requests. For example, when you are on your favorite browser and look up YouTube.com, you are making an HTTP "get" request to the server to load the contents from YouTube.com.

❓ What are some examples of request types??

Below are some of the most common HTTP requests. Read through each one, and try to get familiar with their functions!

⭐ Get Request: gets data from a server (hence the name). The data we want is specified using a URL we call a Request URL. In this case, you will use a Get Request URL from the Catass API to receive a cat picture.

⭐ Post Request: used to send data to a server, to create or update a resource. The information submitted to the server is archived in the request body of the HTTP request. This is often used to send user-generated data to a server. An example could be uploading a picture to a Post URL.

⭐ Put Request: similar to a Post Request, but a put request will always have the same result every time you use one, whereas a post request might not. We call this property "idempotency."

⭐ Delete Request: used to delete resources indicated by the URL and will remove the targeted resources.

❓ What are APIs, and what makes one "RESTful?"

An API is a piece of software that lets two other pieces of software talk to each other! Imagine you're driving a car - the wheel and pedals would be like APIs between you and the car, since they allow you to controll the car easily.

Most of the time, an API will be used to connect an app to an external service. For example, the Twitter API could allow a user to automatically receive updates on tweets concerning a particular topic or event.

Any API that follows these 5 rules is, by definition, RESTful.

You don't need to worry about the 5 rules, but if you're curious...

  1. Internally, the API should keep the things the user does and the things the server does separate.
  2. The server shouldn't ever need to store the user's data to function.
  3. All output data from the API should mark itself as either "cacheable" or "non-cacheable" (cacheable data can be stored and reused later by the user, while non-cacheable data should be discarded and recomputed by the API every time).
  4. The user shouldn't be able to tell whether or not they're communicating with the API's server, or an intermediary server.
  5. The interface of the API should conform to a few agreed-upon conventions (that we won't be going over here).

2: Sending Requests with Postman

In this step, we'll be using a desktop application called Postman to test an API. Postman is a debugging tool for RESTful APIs, which allows you to test both pre-existing, community made APIs, or your own self-made APIs, without having to write any HTML test code!

https://media.giphy.com/media/Kxs6TTeI4siCJKKRMC/giphy.gif

Postman offers a web tool to test API's, but during the camp we will be using the desktop application.

  • You can sign up for Postman here.
  • Download and install the desktop application here
  • Read the "Getting Started" documentation here
❓ How do we build requests?

The Postman documentation covers:

  • Creating requests
  • Adding request detail
  • Setting request URLs
  • Selecting request methods
  • Sending parameters
  • Sending body data
  • Authenticating requests
  • Configuring request headers

Open up the Postman application and try it out yourself:

❓ How do I specify the API Endpoint?

Enter https://cataas.com/cat/cute/says/Serverless, which is the API endpoint, into the text box next to GET

image



❓ How do I set the parameters?

Click on "Params" and enter color into Key and the color you want (eg. blue) into Value. Enter size into the next Key row and a number (eg. 50) into Value.

Note on parameters:

  • the size parameter refers to the font size of your caption. It has a limit at around 1,200.
  • Colors are pretty hit or miss; since the Cat API is on the web, but it generally adheres to HTML color names. Expect values such as "blue, green, yellow" to work.
  • The API can take very large words as input for the caption, however only 34 characters can be seen on the picture at one time .



Click Send to get your cat picture

📹 Walkthrough Video

walkthrough video

Week 4: Ultimate CAT INVASION!

Week 4 Step 7 ⬤⬤⬤⬤⬤⬤⬤ | 🕐 Estimated completion: 5-20 minutes

Ultimate CAT INVASION! ~

Demo: 🐱

It's now time to connect everything together! (catzapp/index.html, catzapp/script.js, and your Azure function)

✅ Task:

  • 1: Make sure you're on the twocatz-frontend branch
  • 2: Create 4 input boxes to accept 4 names, each with an id of name1, name2, name3, and name4 respectively
  • 3: Modify catzapp/script.js so that it calls your Azure Function with the 4 names as parameters
  • 4: Create 4 img tags, each with an id of image1, image2, image3, and image4 respectively
  • 5: Display the outputs of the Azure Function as images in the tags
  • 6: Commit your updated changes to catzapp/index.html and catzapp/script.js
  • 7: Create a pull request to merge twocatz-frontend onto main, and only merge the pull request when the bot approves your changes!

💡 If your test doesn't pass the first time, it might be because your Azure Function is slow to respond. Simply re-run the job like this.

🚧 Test your Work

Open up your LiveServer plugin and start your local server. To test your web app:

⚠️ When you enter FOUR NAMES into each of the text boxes, do FOUR CAT PICTURES show up below?

1: How can Base64 turn into a Cat?

Now that you should've received strings of base64 from your Azure function call, you need some way to display them for the users.

❓ How can I display base64?
  1. Retrieve the base64 values from your API
  2. Append data:image/png;base64, in front of the base64 data
  3. Like you've done previously, modify the src attribute of the image tags and set it equal to the string you created in 2️⃣

Read more here


📹 Walkthrough Video

walkthrough video

Week 3: Exposed!!

Week 3 Step 8 ⬤⬤⬤⬤⬤⬤⬤⬤◯ | 🕐 Estimated completion: 10-20 minutes

Exposed!!

✅ Task:

Modify the deepsecrets Azure Function to

  • 1: Query for all secrets stored in the CosmosDB
  • 2: Select a random secret to return in the body
  • 3: Commit your updated function code to deepsecrets/index.jsto the deepsecrets branch
  • 4: Create a pull request to merge deepsecrets onto main, and only merge the pull request when the bot approves your changes!

❗ Make sure to return your message in this format:

Thanks 😊! Stored your secret "insert_user's_message". 😯 Someone confessed that: "a_random_secret"

Remember that the bot will be checking the response, so make sure you use the same emojis, punctuation, wording, and capitalization! Only the messages can be different.

🚧 Test Your Work

To test your work, send your Twilio number a text message. You should still receive a text back that contains a message with the same format and wording as the one below, but now the second secret returned will be random, and no longer the most recent secret stored!

Thanks 😊! Stored your secret "insert_user's_message". 😯 Someone confessed that: "a_random_secret"

💡 Yay! This means you've successfully queried for and returned a random secret.


1: Modifying the SQL Query

In your createDocument function, you'll first want to modify the original SQL query to now query for all secrets inside your container.

❓ How do I select all secrets?
const querySpec = {
    query: "SELECT * from c"
};

2: Selecting a Random Item

Next, you'll want to select a random secret by:

  1. Generating a random integer that is strictly less than the length of the array of secrets. (Hint: use Math.floor() and Math.random())
  2. Returning the secret at an index of this random number.
❓ How do I generate a random number for the index?

The Math.floor() function returns the floor of the given number - ie. the largest integer less than or equal to a given number. In the example below, the generated random number will never be greater than items.length.

let random_value = Math.floor(items.length * Math.random());

💡 Make sure to change your responseMessage to return the correct index of items.

📹 Walkthrough Video

walkthrough video

Week 1: Say Hello to VSCode

Week 1 Step 2 ⬤⬤◯◯◯◯◯◯◯ | 🕐 Estimated completion: 5-20 minutes

Say "Hello" to VS Code

✅ Tasks:

  • 1: Download Visual Studio Code and install the Azure Tools and Live Server extensions
  • 2: Clone this repo using the terminal on VS Code and create a new branch named hello
  • 3: In a new file called week1/helloworld.js, write and export a JS function hello that returns "Hello World"
  • 4: Commit your changes to the hello branch
  • 5: Create a pull request that merges hello to main, let CounselorBot check your code, and only merge the pull request if the bot approves it!

The Merge button will turn green when the bot approves the pull request.

💡 Tip: When we tell you to name a file called directory/thefile.js, the first part before the / is a directory (otherwise known as a folder).

🚧 Test your Work

If you run node helloworld.js in the terminal, the output should be Hello World

Note: From now on, you will never need to close an issue. The Counselor will do that for you, create new issues, and new comments for further instructions!

❌ My code failed, what do I do?

No worries, check this out to help resolve the issue.

💡 TIP: If you want to re-run a check without comitting something else, check this out.

1: The IDE - Visual Studio Code

An IDE is a software application that provides comprehensive facilities to computer programmers for software development. An IDE normally consists of at least a source code editor, build automation tools, and a debugger. Although there are hundreds of IDEs to choose from, we are going to use Visual Studio Code due to its popularity and integration with Azure (via extensions and libraries).

💻 How to set up your VS Code Environment

Download VS Code for your operating system and check out this tutorial before getting started.

Inside VS Code, download the following extensions:

  • Azure Tools (includes Azure Account, Azure App Service, Azure Functions)
  • Live Server

All of the Azure extensions allow you to work on your Azure Function App in VS Code instead of working directly through the Microsoft portal. Live Server is a quick and temporary testing server, and you can use it to test HTML pages. To launch, right click on your html file and press "Open with Live Server" or click "Go Live" in the bottom right corner:

Screen Shot 2021-01-10 at 1 53 20 PM Screen Shot 2021-01-10 at 1 53 40 PM

💡 Note: Dark Theme is our personal favorite, but feel free to choose whichever theme you like best. Go to this site to view your options!

2: GitHub with VS Code

Check out this awesome documentation about how to set up Git on your local computer

❓ How do I use GitHub on VS Code?
  1. Once you have complete the steps in the documentation, clone this repo on your computer
  2. Use the following commands to work with branches in the terminal:
    • Check which branch you're in: git branch
    • Create a new branch and change into it: git checkout --b name-of-branch
    • Change branch: git checkout name-of-branch
  3. Afterwards, follow this tutorial by VS Code on connecting to GitHub straight from the app!

‼️ Don't forget to git pull before making any changes to your local repo!!

3: Writing and Exporting a JavaScript function

JavaScript enables the ability to export functions in a program so that you can access them in other parts of the program via the import statement. In this case, we want to export your programs in order for CounselorBot to check your code.

❓ What are JavaScript and Node.js?
JavaScript is the language of the internet! It is a powerful tool for creating complex web apps. However, JavaScript can be used for building the client for applications, and sometimes requires a way to access this client, which is also known as the server-side. Node.js is the solution to this problem, and allows you to write and run code not linked to a website locally.

❗ Make sure you have Node.js and npm installed before moving forwards: https://www.npmjs.com/get-npm. As we are using Azure Functions make sure that you install a supported LTS release of Node.js. You find the currently supported releases in the Azure Functions documentation.

  • Check if Node.js is installed: run node -v in your terminal
  • Check if npm is installed: run npm -v in your terminal

If you would like to read more, refer to this article on JavaScript and this article on Node.js.


❓ How do I export a function?
Let's say your function name is `hello`. To export it, add this line of code at the very bottom of your file outside of your function: `module.exports = hello`.

Example:

function hello() {
    // your code
} 

module.exports = hello

When you commit the file, we will try to run the function by importing it and compare it's output to the expected output like so:

const hello = require('../../week1/helloworld.js')
const output = hello()

How does this apply to code in the real world?

Just like you can import code from modules other people have written, you can also import functions you wrote from other files to reuse them. In function oriented programming, you use functions over and over again to save code. If you want to use the function hello() in another file, you would need to import it.


❓ Can I have more detailed steps?
  1. Create a new file
  2. Name the file helloworld.js
  3. Write your code
  4. If you have Node.js installed on your computer, open terminal on VS Code and type 'node helloworld.js'
  5. If you have not installed Node.js on your computer, you will need to do that first: https://nodejs.org/en/download/
  6. Tip: to test your function, call it in your code.
  7. Create a new branch named week1 and commit your helloworld.js file in the root directory.


💡 Try to not use the web editor! Commit from your command line.

4: Committing to a repository using a command line?

Start out by downloading Git. Then, open your command line.

The Commands

Navigate to the directory in your command line where you want to keep your repository.

Tip: Use cd ./your-directory to change directories, pwd to find out where you are, and ls to list files & directories. More information is here.

Cloning your repository Click on "Code" on your repo's page and find your repo's HTTP link:

Use the git clone command and replace the url to get your repository's files onto your local computer:

git clone https://github.com/example/example.git

Now is the time to make your changes to your code!


Committing and pushing code First, "stage" your changes. You will be specifying what files you want to commit the changes of.

Stage helloworld.js changes only:

git add helloworld.js

Stage all your changes to the repository:

git add *

Next, let's commit the code. Usually, your commits will be a group of changes that make sense together. Add a description!

git commit -m "insert your description"

Save your commits to the repository on Github!

git push

For more information, refer to this link


Congrats! Your changes should now be visible on Github

Don't forget to git pull before making any changes to your local repo!! This gets any changes that were made by the bot.

📹 Walkthrough Video

Alt text

Building Bunnimage: The Frontend

Week 4 Step 1 ⬤◯◯◯◯◯◯ | 🕐 Estimated completion: 20-25 minutes

Adding an Event Listener to your HTML Form!

Demo: 🐰

Congrats! If you made it this far, your Azure functions can now return data. However, users won't want to make POST requests with Postman to use your API though, so let's make it more user friendly.

✅ Task:

  • 1: Create a bunnimage-frontend branch
  • 2: Install LiveServer for testing
  • 3: Optional Learn HTML basics with the interactive course
  • 4: Write JavaScript to listen for the form to be submitted and call a function
  • 5: Write a function in index.js to display input appended with a ❤️ in the output div
  • 6: Commit your code to bunnimage/index.html and bunnimage/script.js
  • 7: Create a pull request that merges bunnimage-frontend to main, and only merge the pull request when the bot approves your changes!

🚧 Test your Work

Use LiveServer! This is a helpful VSCode extension that allows you to see your HTML that updates while you edit it.

❓ How do you use LiveServer?

image

  • To start a local server, click Go live at the bottom right of the screen, as shown in the image.
    • Make sure that you have the entire repo open on VS Code and not just the individual files.
    • If this is your first time installing LiveServer, you might need to close/quit VS Code and reopen it.
  • Test it out, and see what your HTML page looks like! It's OK if it's boring, so feel free to style it with CSS!

‼️ Create & Reference index.js in index.html

❓ How do I reference index.js in index.html?

Put it at the very end of the body tag!

    <script src="index.js" type="text/javascript"></script>

1: Writing the Event Handler Function!

On websites, there are many different types of events that can occur. For example:

  • The user selects a certain element or hovers the cursor over a certain element.
  • The user chooses a key on the keyboard.
  • A form is submitted.
  • Curious for more? Look here

We need to create a function which triggers when someone submits an image,
but first we will create a function which changes content on the page when we
submit text.

I'm confused on how to do this
  • In a new file named index.js, create a variable using the id of the HTML form:
const bunnForm = document.getElementById('bunnForm');
  • In your index.js file, create an event listener with an anonymous function.
    Inside of the function, create a variable for the value that was input in
    the text box.
bunnForm.addEventListener('submit', function (event) {
  const username = document.getElementById("username").value
});

💡 We are retrieving the value of the "username" text box with this code!
💡 The document.getElementById() method gets the value of the input text from the HTML.

  • Create a variable to target html div with the id of "output" and use the
    textContent property to add a "❤" to the value that was input for username.

💡 By default, the website will reload whenever a user clicks submit. This leads to our text on the page that we set being cleared away. We can prevent this by adding this to the top of our function.

event.preventDefault()
bunnForm.addEventListener('submit', function (event) {
   event.preventDefault()
   const username = document.getElementById("username").value
   const output = document.getElementById("output")
   output.textContent = username + "❤"
});

2: How do I trigger the function when the form is submitted?

The event listener triggers the JavaScript function when the form is submitted.
Here is how the form should look in the bunnimage index.html file:

<form id="bunnForm">
   <label for="name">Code: </label> <br>
   <input type="text" id="username" name="name" aria-label="name" /> <br>
   <input value="Submit" type="submit" />
</form>
<br/>
<div id="output"></div>

Here is the JavaScript which should be in a separate JavaScript file index.js:

const bunnForm = document.getElementById('bunnForm');

bunnForm.addEventListener('submit', function (event) {
   event.preventDefault()
   const username = document.getElementById("username").value
   const output = document.getElementById("output")
   output.textContent = username + "❤"
});

💡 the for attribute in the <label> and the aria-label attribute in the <input>element make the website more accessible to users needing assistive technologies such as screen readers.

📹 Walkthrough Video

walkthrough video

Week 1: Trying to make node-fetch happen

Week 1 Step 6 ⬤⬤⬤⬤⬤⬤◯◯◯ | 🕐 Estimated completion: 25-35 minutes

Trying to make node-fetch happen

✅ Task:

  • Run git pull and create a new branch called twocatz
  • 1: Create and deploy an HTTP trigger Azure Function that creates a request for an image from the CataaS API (https://bit-cat.azurewebsites.net/cat/says/serverless)
  • 2: Return the image in the request body encoded in base64 in JSON format
  • 3: Place your function URL in a repository secret called TWOCATZ_ENDPOINT and commit function code in a file named twocatz/index.js to the twocatz branch
  • 4: Create a pull request that merges twocatz to main, and only merge when the bot approves your changes!

💡 Note: Every time you make a new commit to twocatz, we will check your function by making a request.*

🚨 NOTE: If the CataaS API is not working, please use this alternate endpoint: https://bit-cat.azurewebsites.net/cat/says/serverless

🚧 Test your Work

When you paste your Function URL in your browser or make a GET request with Postman, you might get something like:

{
    "/9j/4AAQSk..."
}

1: Making requests in Azure

To make a request to the CataaS API, you can try using the node-fetch module, but there are many other ways to do it as you can see here.

❓ How can I make a request to the CataaS API?

Let's use the node-fetch module for this task.

‼️ Make sure you are in the directory of your Azure function to run these commands.

  1. Install the module in terminal using the following commands in order:

    npm init -y 
    
    npm install node-fetch@2
  2. Add it to your code:

    Add this line of code to reference the module at the top of your code (outside of the function): const fetch = require('node-fetch')

  3. Make the request!

    Add the following code within the function:

    const resp = await fetch(THE_ENDPOINT, {
        method: 'GET'
    });
    
    const data = await resp.arrayBuffer()
    // we need to receive it as a buffer since this is an image we are receiving from the API
    // Buffer?? https://developer.mozilla.org/en-US/docs/Web/API/Blob
  4. What should you place in place of THE_ENDPOINT? Change the code.




2: Return your images encoded in base64!

❓ What does it mean to encode something in base64?

Base64 is just another way to represent data. We can also represent the number 11 or 0 in base64. Remember that the images you see on your screen are actually just numbers!

When we're coding websites, we can use base64 to display images on websites. The base64 outputted from your API can be used to create this:

image

Base64 encoding allows programs to encode binary data into text (ASCII characters) in order to prevent data loss. We do this since there are certain transfer channels that only reliably transfer text data, and this encoding method allows us to safely transfer images in the form of text.



❓ How can I encode data in base64?
base64data = Buffer.from(originaldata).toString('base64')
//put what you want to turn into base64 inside "originaldata"
//"originaldata" will be encoded in base64.



For fun: Once your API successfully returns the images in base64, you can see what they look like using this website.

❓ Now that I've got the picture in base64, how do I return it?

context.res is the key to answering this question!

context.res = {
    body: { your_picture_in_base64 }
}

💡 You need to put brackets to return the data in json format.


📹 Walkthrough Video

walkthrough video

Week 4: Making fetch Happen

Week 4 Step 3 ⬤⬤⬤◯◯◯◯ | 🕐 Estimated completion: 20-25 minutes

Making fetch Happen

Demo: 🐰

We are going to how use the form to call our Azure Function! Yay 🎉! Things are coming together.

✅ Task:

  • 1: Modify the function and use await fetch to send the form input to your Bunnimage Azure Function
  • 2: If the upload was successful, change the output div to Your image has been stored successfully!
  • 3: Modify your CORS settings in your function app so your frontend can make requests.
  • 4: Commit your updated code to bunnimage/script.js to the bunnimage-frontend branch
  • 5: Create a pull request to merge bunnimage-frontend onto main, and only merge the pull request when the bot approves your changes!

🚧 Test your Work

Open up your LiveServer plugin and start your local server. To test your web app:

  1. Upload an image and click submit. Do you get a "Your image has been stored successfully!" message?
  2. Log into your Azure portal and navigate to your storage account. In your container, do you see your image?

1: How do I get form data?

To get the information the user entered in the form, you will need to create a FormData object. FormData lets you compile a set of key/value pairs to send.

const bunniForm = document.getElementById("YOUR_FORM_ID");
const payload = new FormData(bunniForm);
const file = fileInput.files[0]; // fileInput is the file upload input element
payload.append("file", file);
❓ What's happening here?

Code breakdown:

  • we reference the html form element with document.getElementById("YOUR_FORM_ID"), so we can create a FormData object.
  • we extract the file the user uploaded using fileInput.files[0]
  • finally, we add the file created to the FormData object using append, and a key-value pair. The key is file, and its value is the file itself.

💡 Since we need to reference the form element in document.getElementById("YOUR_FORM_ID"), we need to give it an id in the html.

<form enctype="multipart/form-data" id="YOUR_FORM_ID">

Now, we can send the payload to our azure function in the body of our fetch request.


2: How do I call the Azure Function?

The Fetch API is built in to Javascript to make HTTP requests (using GET, POST and other methods), download, and upload files.

💡 We don't have to install node-fetch as an npm package since fetch is built into JavaScript. Remember: we were using NodeJS before.

To start a request, call the special function fetch():

const response = await fetch(resource, options);

Fetch accepts two arguments:

  • resource: the URL string, or a Request object
  • options: the configuration object with properties like method, headers, body, credentials, and more.
❓ What does fetch exactly do?

fetch() starts a request and returns a promise. When the request completes, the promise is resolved with the Response object. If the request fails due to some network problems, the promise is rejected.

Keep this in mind if your function throws a promise error!


3: How to configure "options"

In order to pass the type of method, the payload in the pody, and headers, you have to configure the "options" part of the fetch function.

const response = await fetch(resource, {
   method: ... ,
   body: ... , 
   headers: { ... }
});

Fill in the method, headers and body with the correct information compatible with your API!

💡 Refer back to previous Fetch requests we made in NodeJS for hints. Remember for this request, we are not receiving JSON!

How to validate that the upload worked

We need to let the users know if the upload was successful! We do this by making sure we receive a response from our upload API:

❗ Use a "try...catch"

A try...catch is what it sounds like - it catches errors. This makes for a better user experience and is crucial to error handling.

try { 
    // Try receiving data from your fetch request
    // Change the output div's value
} catch (e) {
    // If an error occurred, tell the user!
}



4: Update CORS Settings

CORS (Cross-origin resource sharing) settings tells the Function App what domains (such as wwww.website.com) can make requests to the Azure Functions.

❓ How can I change it?

Head to your function app in your portal and find the button on the left hand side called CORS.
https://user-images.githubusercontent.com/69332964/99188905-6f0b7c00-272c-11eb-8142-f91882227c78.png


❓ What should I change it to?

Change it to a wildcard operator (*), which allows all origin domains to make requests.

💡 Be sure to remove any other existing inputs before attempting to save with wildcard. Don't forget to save your changes!


📹 Walkthrough Video

Part 1

walkthrough video

Part 2

walkthrough video

A Song 4 U

Week 2 Step 6 ⬤⬤⬤⬤⬤⬤◯◯ | 🕐 Estimated completion: 10-15 minutes

Pose! Song Recommendation with Age Recognition

✅ Task:

Create a new Azure Function that parses a Twilio request body and returns the image url.

  • Create a new branch called song4u
  • 1: Create a Twilio account
  • Install npm package qs
  • 2: Create a new Azure Function called song4u that acts as a Twilio webhook
  • 3: Return the image url from the POST request
  • 4: Commit your updated function code to song4u/index.js on the song4u branch and add your function url to a repository secret named SONGREC_ENDPOINT
  • 5: Create a pull request that merges song4u to main, and only merge the pull request when the bot approves your changes!

🚧 Test Your Work

To test your work, try texting a jpg image to your Twilio number. You should receive a link back that contains the image.

❓ How do I find my Twilio number?

See your phone numbers.

1: Creating a Twilio Account

In this project, the user will text an image to a Twilio number and an Azure function will analyze the image. The Face API will determine the age of the person in the image. We need to create a Twilio account first so that the user can text an image to your Twilio number.

Sign up for a free Twilio trial account here.

❓ What's the process like for signing up for Twilio?
  • When you sign up, you'll be asked to verify your personal phone number. This helps Twilio verify your identity and also allows you to send test messages to your phone from your Twilio account while in trial mode.

  • Once you verify your number, you'll be asked a series of questions to customize your experience.

  • Once you finish the onboarding flow, you'll arrive at your project dashboard in the Twilio Console. This is where you'll be able to access your Account SID, authentication token, find a Twilio phone number, and more.

Learn more about it here!

After we get our account set up, we need to get a phone number, which is free from your trial account.

❓ Where can I get a phone number?
  1. Create a new project on Twilio.

  2. Add a brand new number for your Twilio project, which will be used to send texts to users. Save this to use for later.

Before you move on, be sure to create a new Azure function!

2: Configuring the Azure Function

Our new Azure function will act as a Twilio webhook.

💡 A Twilio webhook is the endpoint that will receive a POST request whenever a Twilio number is used or a text is sent. Twilio sends an HTTP request to a webhook whenever something happens (such as when a text is sent).

You will need to place your function's URL into the settings on your Twilio account.

image

Do you like the command line more? Try using the Twilio CLI instead!

❓ How do I configure it?
  1. Go to the Twilio Console's Numbers page

image

  1. Click on the phone number you'd like to modify

  2. Scroll down to the Messaging section and the "A MESSAGE COMES IN" option.

  3. Paste in your Azure Function URL. Make sure to click Save afterwards!!

image

3. Coding the Azure Function

Test the webhook out!

If you want more insight into how a webhook actually works, try this:

❗ Log the request body in your console using `context.log(req.body)` and text your Twilio number.

Using this code:

module.exports = async function (context, req) {
    const reqbody = req.body
    context.log(reqbody)

    context.res = {
        // status: 200, /* Defaults to 200 */
        body: reqbody
    };
}

You might get something like this:

ToCountry=US&MediaContentType0=image%2Fjpeg&ToState=MI&SmsMessageSid=MM0fe83458b74a1f626eb0da4685ab28b5&NumMedia=1......

In order to parse the parameters from those values you just saw, we need an npm package.

❓ How do you install qs

We need to install the npm package qs. This package parses query strings, and we will use it to parse the SMS sent by the user to the Twilio number so we can access the image sent in the text.

💡 You only need to use npm init -y if you are installing npm packages for the first time on an Function App!

As we did when we installed parse-multipart, we need to enter npm init -y (which initializes the package.json files) before we can install qs:


Screen Shot 2021-05-30 at 7 11 28 PM

Now we can install qs by entering npm install qs:


Screen Shot 2021-05-30 at 8 23 18 PM


❗ The parameter value you want to parse with qs and return from the Azure Function in the body is called MediaUrl0.

Now, to use qs, first don't forget to initialize it!

const querystring = require('qs');

Then, let's parse the request body, which is held in req.body.

const queryObject = querystring.parse(req.body);

From this outputted object, we'll find its MediaUrl0 attribute and return it.

❓ How do I return the output?
context.res = {
   body: queryObject.MediaUrl0
};

📹 Walkthrough Video

walkthrough video

Reviewing your Flowchart

Final Project Step 3 ⬤⬤⬤◯◯◯ | 🕐 Estimated completion: 20-40 minutes

Reviewing your Flowchart

✅ Task:

  • Ask your mentor (or a friend) to carefully look over the flowchart and explain the project to you + take note if there are any holes in their explanation that you still need to visualize in the flowchart for it to make more sense
  • Explain the flowchart back to them and ask them if they have any feedback on how you can improve the use of technologies in your project
  • Use the feedback you improve your flowchart and update the file in the final-project branch
  • 🚀 Commit your changes to the final-project branch, make a pull request, and merge it
    • In the pull request description, explain the feedback you received and the changes you made

How to improve a flowchart

There are 3 main things to focus on when you are getting feedback on and improving your flowchart:

1️⃣ Logic:

  • Does the flowchart make sense?
  • Do the arrow directions makes sense?
  • Are the descriptions for the interactions between technologies logically correct?

2️⃣ Accuracy:

  • Did you include all components required for this project?
  • Do the arrows represent accurate relationships?
  • Are the descriptions accurate to what purposes the components serve?

3️⃣ Readibility and visual appeal:

  • Is it easy to understand what is going on? Is there an easier way to explain it?
  • Are the colors/shapes simple and easy to read?
  • If you are using color codes, do you have a legend that describes what the colors mean?
  • Does the flowchart have a title?

Building Bunnimage: The Frontend

Week 4 Step 1 ⬤◯◯◯◯◯◯ | 🕐 Estimated completion: 20-25 minutes

Adding an Event Listener to your HTML Form!

Demo: 🐰

Congrats! If you made it this far, your Azure functions can now return data. However, users won't want to make POST requests with Postman to use your API though, so let's make it more user friendly.

✅ Task:

  • 1: Create a bunnimage-frontend branch
  • 2: Install LiveServer for testing
  • 3: Optional Learn HTML basics with the interactive course
  • 4: Write JavaScript to listen for the form to be submitted and call a function
  • 5: Write a function in index.js to display input appended with a ❤️ in the output div
  • 6: Commit your code to bunnimage/index.html and bunnimage/script.js
  • 7: Create a pull request that merges bunnimage-frontend to main, and only merge the pull request when the bot approves your changes!

🚧 Test your Work

Use LiveServer! This is a helpful VSCode extension that allows you to see your HTML that updates while you edit it.

❓ How do you use LiveServer?

image

  • To start a local server, click Go live at the bottom right of the screen, as shown in the image.
    • Make sure that you have the entire repo open on VS Code and not just the individual files.
    • If this is your first time installing LiveServer, you might need to close/quit VS Code and reopen it.
  • Test it out, and see what your HTML page looks like! It's OK if it's boring, so feel free to style it with CSS!

‼️ Create & Reference index.js in index.html

❓ How do I reference index.js in index.html?

Put it at the very end of the body tag!

    <script src="index.js" type="text/javascript"></script>

1: Writing the Event Handler Function!

On websites, there are many different types of events that can occur. For example:

  • The user selects a certain element or hovers the cursor over a certain element.
  • The user chooses a key on the keyboard.
  • A form is submitted.
  • Curious for more? Look here

We need to create a function which triggers when someone submits an image,
but first we will create a function which changes content on the page when we
submit text.

I'm confused on how to do this
  • In a new file named index.js, create a variable using the id of the HTML form:
const bunnForm = document.getElementById('bunnForm');
  • In your index.js file, create an event listener with an anonymous function.
    Inside of the function, create a variable for the value that was input in
    the text box.
bunnForm.addEventListener('submit', function (event) {
  const username = document.getElementById("username").value
});

💡 We are retrieving the value of the "username" text box with this code!
💡 The document.getElementById() method gets the value of the input text from the HTML.

  • Create a variable to target html div with the id of "output" and use the
    textContent property to add a "❤" to the value that was input for username.

💡 By default, the website will reload whenever a user clicks submit. This leads to our text on the page that we set being cleared away. We can prevent this by adding this to the top of our function.

event.preventDefault()
bunnForm.addEventListener('submit', function (event) {
   event.preventDefault()
   const username = document.getElementById("username").value
   const output = document.getElementById("output")
   output.textContent = username + "❤"
});

2: How do I trigger the function when the form is submitted?

The event listener triggers the JavaScript function when the form is submitted.
Here is how the form should look in the bunnimage index.html file:

<form id="bunnForm">
   <label for="name">Code: </label> <br>
   <input type="text" id="username" name="name" aria-label="name" /> <br>
   <input value="Submit" type="submit" />
</form>
<br/>
<div id="output"></div>

Here is the JavaScript which should be in a separate JavaScript file index.js:

const bunnForm = document.getElementById('bunnForm');

bunnForm.addEventListener('submit', function (event) {
   event.preventDefault()
   const username = document.getElementById("username").value
   const output = document.getElementById("output")
   output.textContent = username + "❤"
});

💡 the for attribute in the <label> and the aria-label attribute in the <input>element make the website more accessible to users needing assistive technologies such as screen readers.

📹 Walkthrough Video

walkthrough video

Week 4: Downloading your image!

Week 4 Step 4 ⬤⬤⬤⬤◯◯◯ | 🕐 Estimated completion: 5-20 minutes

Downloading your image!

Demo: 🐰

Write and connect a JS file to make a GET request to the download Azure function that includes the codename to receive the link. Edit the button on the HTML page so that it downloads the file when clicked.

✅ Task:

  • 1: Make sure you're on the bunnimage-frontend branch
  • 2: Add an attribute of id with the value button1 onto the upload button
  • 3: Create a new input field with an id of downloadusername and create a button with an id of button2 that calls the function downloadImage() in index.html
  • 4: Create the downloadImage() function with a await fetch GET request to send downloadusername to your Bunnimage Azure Function to return the file download uri
  • 5: If the fetch was successful, open the file download link using window.open() and make sure it replaces the page instead of opening a new tab.
  • 6: Commit your updated code to bunnimage/index.html and bunnimage/script.js
  • 7: Create a pull request to merge bunnimage-frontend onto main, and only merge the pull request when the bot approves your changes!

🚧 Test your Work

Open up your LiveServer plugin and start your local server. To test your web app:

⚠️ Type in the username for an image you have uploaded previously. Does the download button link to your file?

1: Opening a link

window.open() is fairly straightforward to use. Here's the syntax:

window.open(URL, name, specs, replace)

⭐ We will only be using URL and name. Use this documentation to fill it in.

📹 Walkthrough Video

walkthrough video

Week 4: Making fetch Happen

Week 4 Step 3 ⬤⬤⬤◯◯◯◯ | 🕐 Estimated completion: 20-25 minutes

Making fetch Happen

Demo: 🐰

We are going to how use the form to call our Azure Function! Yay 🎉! Things are coming together.

✅ Task:

  • 1: Modify the function and use await fetch to send the form input to your Bunnimage Azure Function
  • 2: If the upload was successful, change the output div to Your image has been stored successfully!
  • 3: Modify your CORS settings in your function app so your frontend can make requests.
  • 4: Commit your updated code to bunnimage/script.js to the bunnimage-frontend branch
  • 5: Create a pull request to merge bunnimage-frontend onto main, and only merge the pull request when the bot approves your changes!

🚧 Test your Work

Open up your LiveServer plugin and start your local server. To test your web app:

  1. Upload an image and click submit. Do you get a "Your image has been stored successfully!" message?
  2. Log into your Azure portal and navigate to your storage account. In your container, do you see your image?

1: How do I get form data?

To get the information the user entered in the form, you will need to create a FormData object. FormData lets you compile a set of key/value pairs to send.

const bunniForm = document.getElementById("YOUR_FORM_ID");
const payload = new FormData(bunniForm);
const file = fileInput.files[0]; // fileInput is the file upload input element
payload.append("file", file);
❓ What's happening here?

Code breakdown:

  • we reference the html form element with document.getElementById("YOUR_FORM_ID"), so we can create a FormData object.
  • we extract the file the user uploaded using fileInput.files[0]
  • finally, we add the file created to the FormData object using append, and a key-value pair. The key is file, and its value is the file itself.

💡 Since we need to reference the form element in document.getElementById("YOUR_FORM_ID"), we need to give it an id in the html.

<form enctype="multipart/form-data" id="YOUR_FORM_ID">

Now, we can send the payload to our azure function in the body of our fetch request.


2: How do I call the Azure Function?

The Fetch API is built in to Javascript to make HTTP requests (using GET, POST and other methods), download, and upload files.

💡 We don't have to install node-fetch as an npm package since fetch is built into JavaScript. Remember: we were using NodeJS before.

To start a request, call the special function fetch():

const response = await fetch(resource, options);

Fetch accepts two arguments:

  • resource: the URL string, or a Request object
  • options: the configuration object with properties like method, headers, body, credentials, and more.
❓ What does fetch exactly do?

fetch() starts a request and returns a promise. When the request completes, the promise is resolved with the Response object. If the request fails due to some network problems, the promise is rejected.

Keep this in mind if your function throws a promise error!


3: How to configure "options"

In order to pass the type of method, the payload in the pody, and headers, you have to configure the "options" part of the fetch function.

const response = await fetch(resource, {
   method: ... ,
   body: ... , 
   headers: { ... }
});

Fill in the method, headers and body with the correct information compatible with your API!

💡 Refer back to previous Fetch requests we made in NodeJS for hints. Remember for this request, we are not receiving JSON!

How to validate that the upload worked

We need to let the users know if the upload was successful! We do this by making sure we receive a response from our upload API:

❗ Use a "try...catch"

A try...catch is what it sounds like - it catches errors. This makes for a better user experience and is crucial to error handling.

try { 
    // Try receiving data from your fetch request
    // Change the output div's value
} catch (e) {
    // If an error occurred, tell the user!
}



4: Update CORS Settings

CORS (Cross-origin resource sharing) settings tells the Function App what domains (such as wwww.website.com) can make requests to the Azure Functions.

❓ How can I change it?

Head to your function app in your portal and find the button on the left hand side called CORS.
https://user-images.githubusercontent.com/69332964/99188905-6f0b7c00-272c-11eb-8142-f91882227c78.png


❓ What should I change it to?

Change it to a wildcard operator (*), which allows all origin domains to make requests.

💡 Be sure to remove any other existing inputs before attempting to save with wildcard. Don't forget to save your changes!


📹 Walkthrough Video

Part 1

walkthrough video

Part 2

walkthrough video

Starting your Final Project

Final Project Step 1 ⬤◯◯◯◯◯ | 🕐 Estimated completion: 1-2 hours

Laying out the groundwork

✅ Task:

  • Create a new branch called final-project
    • Commit all of your final project related changes in the final-project branch
  • Follow the template in "project/tech.md" file and plan out your project (directly edit the file)
    • Keep all of your final project related files in the "project" folder
  • 🚀 Commit your changes to the final-project branch, make a pull request, and merge it

Planning it out

  1. In that tech.md, create a list of the Azure services you expect to create/use for your project and what specific purpose they serve for your project
  2. Create a list of the external APIs you expect to create/use for your project and what specific purpose they serve for your project
  3. Create a list of the npm packages, libraries, and/or databases you expect to create/use for your project and what specific purpose they serve for your project
  4. Create a list of the frontend languages you expect to create/use for your project and what specific purpose they serve for your project

Preparing a Checklist

As a beginner developer, it is vital that you consider the pros and cons of different technologies before you decide to use them. In this case, you are relatively profient with Azure functions and certain APIs, but for your final project you might find yourself looking into new softwares that you have't interacted before, talking to professionals about them, reading their documentation, and using them in your project.


this you?

You will need to be able to justify your decisions confidently in order to prevent making decisions that will cost you excess time and effort in the developing phase. For this, you will need to start conducting extension research on how the technologies work and how they will be interacting with each other.

Researching APIs (and other technologies)

There are thousands of amazing APIs that all have various functions, and there is no limit to the number of APIs you can use for your project.

How to find the right API?

  1. Start with a simple Google search or filter through online lists such as rapidapi.com if you already know what purpose you want your API to serve (e.g. find a location, send an SMS, ...)
  2. Research lists such as rapidapi.com that have tags/categories that you can filter through to get some ideas if you're looking for 💡 project inspiration 💡 through cool new APIs
  3. Talk to people in the industry or try watching some API workshops to see what is out there

Popular API categories

  • SMS notifications (e.g. Twilio API)
  • Location based APIs
  • Food related APIs
  • Weather APIs

Examples of projects and which APIs they used

Although these are great places to start, you might want to spice up your own project by using less conventional APIs as well that give your project an edge and make them stand out. This project is your crown jewel from the Serverless Camp. Make it memorable and have fun with it!

Week 2: Getting Emotional Calling the Giphy API

Week 2 Step 5 ⬤⬤⬤⬤⬤◯◯◯ | 🕐 Estimated completion: 20-30 minutes

Getting Emotional ~ Calling the Giphy API

✅ Task:

Call the GIPHY API with the dominant emotion of a picture

  • 1: Create a GIPHY account and save your API key as a Function App Secret
  • 2: In emotionalgifs, call the GIPHY API in a GET request with the dominant emotion
  • 3: Commit your updated function code to emotionalgifs/index.js on the emotionalgifs branch
  • 4: Create a pull request to merge emotionalgifs onto main, and only merge the pull request when the bot approves your changes!

🚧 Test your work

Create a POST request in Postman. Use the function URL as the request URL, and send an image in the body of the request:
Screen Shot 2021-05-30 at 3 07 21 PM

✅ Expected Output
The link outputted by the function should look something like this:

https://giphy.com/gifs/happy-spongebob-squarepants-happiness-brHaCdJqCXijm



1. Using the GIPHY API

We're going to connect the your first Azure function, emotionalgifs, with the GIPHY API.

Creating a GIPHY Account

Set up an account by clicking here and enter an email address, username, and password.

Generating an API Key

According to the documentation, an API key is a required parameters in a call to GIPHY's translate endpoint. The link (for gifs) that is listed in the documentation is the endpoint we will be using in this project.

❓ How do I create an API key?

To create an API key click here and click Create an App.

Screen Shot 2021-04-15 at 5 55 16 PM

Select API, not SDK!

Screen Shot 2021-04-15 at 5 55 32 PM

Then, enter the required information.

Screen Shot 2021-04-15 at 5 55 41 PM

Click Create App, and your key should be given.

Next, store your API key in your Azure Function's environment secrets.

2. Modifying the Azure Function

We will be calling the GIPHY API in the same function that analyzes inputted images.

Create another async function in emotionalgifs called findGifs. It needs a parameter through which we can pass the dominant emotion of an image. Call this parameter emotion.

💡 Use the documentation to create a request to the Giphy API in the function.

❓ How do you call the GIPHY API?

We're going to call the GIPHY API inside our new async function using fetch. Use the translate endpoint from the documentation. HINT: we want the dominant emotion from the image to be the search term, and we only want 1 gif to be returned.

//COMPLETE THE CODE
const apiResult = await fetch ("https://api.giphy.com/v1/gifs/translate?//WHAT GOES HERE?");

Hint: If you read the documentation correctly, you should see that you need to use your API key in the request. Remember to access your environment secrets, you can use process.env['the secret name']

We need to convert the content of apiResult into JSON format. Remember, we're using the await keyword because fetch (which we used to call the GIPHY API) returns a Promise, and a Promise is a proxy for a value that isn't currently known.

const jsonResult = await //WHAT GOES HERE?.json();

Finally, we need to return a specific link from the JSON file stored in jsonResult:

return //WHAT GOES HERE.data.url;



Now that you have a async function that can can the Giphy API with an emotion and return a gif link, you need to incorporate it in the main module.exports function.

TIP: Use await to receive a response from the function since it is async!

❓ How should I call the findGifs function we wrote?

Let's call findGifs in the first async function in emotionalgifs. Currently, our first async function looks like this:

module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    const boundary = multipart.getBoundary(req.headers['content-type']);
    const body = req.body;
    const parts = multipart.Parse(body, boundary);

    let result = await analyzeImage(parts[0].data);

    let emotions = result[0].faceAttributes.emotion;
    let objects = Object.values(emotions);
    const main_emotion = Object.keys(emotions).find(key => emotions[key] === Math.max(...objects));

    context.res = {
        // status: 200, /* Defaults to 200 */
        body: main_emotion
    };
    console.log(result)
    context.done();
}

We need to declare another variable, gif. It needs to store the link returned when our new async function, findGifs, is called. Also, the dominant emotion from our analyzed image needs to be passed through the emotion parameter.

let gif = await //WHAT GOES HERE?

Finally, we need our new variable gif to be the output of emotionalgifs rather than main_emotion:

context.res = {
    // status: 200, /* Defaults to 200 */
    body: //WHAT GOES HERE?
};



📹 Walkthrough Video

walkthrough video

Timeline

Final Project Step 4 ⬤⬤⬤⬤◯◯ | 🕐 Estimated completion: 25 minutes

Timeline

✅ Task:

  • Follow the template in "project/timeline.md" file and develop a draft of your timeline
  • Ask a mentor (or someone with software engineering experience) to review your gameplan
  • Once your timeline is mentor-approved, convert your timeline into issues in your project repo
    • Use the Final Project Issue Template
  • 🚀 Commit your changes to the final-project branch, make a pull request, and merge it

Developing a timeline

Before you start to code, you need to have a overarching plan on how you will build your project. It is completely okay (in fact, probably inevitable), for your plan to change once you start developing, but you still need a place to start.

❓ Where to start

Look back at your checklist and decide where you want to start developing your project. Do you want to start with:

  • UI of the app/webapp
  • Azure function
  • API implementation

The best approach recommended by our mentors is to "start small then build big." If you're really unsure, ask your mentor!

🥧 Break it down

The worst part of any task is not knowing when it's going to end. When you're developing your own timeline, make sure that each task doesn't take you more than 1-2 hours. The recommendation is to have tasks that can individually be completed in less than 25mins.

In reality, they might end up taking more time (especially with debugging involved) but try your best to estimate.

⏰ Scheduling deadlines

Speak with your mentor to make sure you can develop your timeline to maximize your timeline.

Keep in mind that there will be many times when you might not be able to meet your own deadlines. How will you deal with this? Do you have enough buffer time allotted in your timeline for when things don't go according to plan?

Sometimes you might become lucky and end up having extra time on your hands.

How do you plan on maximizing the impact of this time so you can ship the best version of your project at the end?

Be clearn on how to manage your own time and hold yourself accountable! Ask your peers and friends for help if you need it.

📢 Getting feedback

Your mentor knows best! They are professionals who have been working in the tech industry on projects much more complex and that have covered all sorts of timelines and team sizes. Get their feedback on your plan and see if they have any recommendations on how you can build your project more efficiently.

🔄 Converting tasks into issues

Once your timeline is ready to go, task each individual task and create an issue for it. If you really want to go the extra mile, you can try to use the Projects feature on GitHub.

As issue is like a task that you need to work on for your code. Here are some examples:

  • Develop function to store image in database
  • Convert image to base64
  • Fix bug on like 29 in Azure function
  • Document function send_tweets() in file app.js
❗ How do I create a new issue?
Screen Shot 2021-06-03 at 1 32 11 PM
  1. At the top menu, click on Issues.
  2. Click on the green button that called "New Issue"
  3. In the title, write a brief description of what needs to be done (look at the list above)
  4. Within the issue (where it says Write a comment) write down more details (checklist) of what needs to be completed for this issue to be done
  5. Click on the green "Submit new isuse" button


❗ How should I format my issue?
When you're planning, it's good to have an idea of how you want to execute these issues. Below is an example of an issue template (in markdown) that you can use to organize your issues.
### [Task Name]:

#### Description
- [Replace with description]

#### ETA:
> How long do you think it will take to complete this?
- [Replace with eta]

#### Objective:
> Checklist of everything you need to do to complete this issue
- [ ] [Replace with small task  1]
- [ ] [Replace with small task  2]
- [ ] [Replace with small task  3]



Storing files and things with DB

Week 3 Step 1 ⬤◯◯◯◯◯◯◯◯ | 🕐 Estimated completion: 5-20 minutes

Blob Storage & Me

This week, you will be going through steps to set up a Blob storage container along with its account. If you are already familiar with this, feel free to skip to the end and complete the task to move on.

✅ Task:

  • Create a new branch called bunnimage
  • 1: Create a Blob storage account and container with a blob access level
  • 2: Create a new Azure function called bunnimage-upload with the HTTP trigger template and install NPM packages
    • parse-multipart
    • node-fetch
    • @azure/storage-blob (install version 12.2.1)
  • 3: Store your Azure Blob Storage account access keys (container name and connection string) as secrets called container_name and storage_account_connection_string and commit your starter (template) function code to bunnimage-upload/index.js on the bunnimage branch
  • 4: Store the connection string in a function app configuration secret called AZURE_STORAGE_CONNECTION_STRING
  • 5: Create a pull request that merges bunnimage to main, and merge when the bot approves!

1. Creating a Blob Storage Account and its Resources

Azure Blob storage is solution that can store massive amounts of unstructured data, like text, images, or videos. Read more about it here!

A storage account provides a unique namespace in Azure for your data. Every object that you store in Azure Storage has an address that includes your unique account name. The combination of the account name and the Azure Storage blob endpoint forms the base address for the objects in your storage account.

For example, if your storage account is named mystorageaccount, then the default endpoint for Blob storage is: http://mystorageaccount.blob.core.windows.net

❓ How do you create a Blob Storage Account
  1. Navigate to your Azure portal.

  2. In the Search Bar, search and click on "Storage accounts".

    image

  3. Click on "Create storage account".

    image

  4. Fill out the storage account details like below, and click "Review + create".

    image

  5. Click "Create".

    image

  6. Wait for the screen to display "Your deployment is complete". Click "Go to resource". You're ready to create your Blob Storage container!

    image



❓ How do you access your Azure Blob Storage account access key
  1. Navigate to your storage account page.

  2. On the left hand bar, click on Security + networking > Access Keys.

    image

  3. Click "Show keys", and you can copy one of the connection strings' information.



❓ How do you create a Blob Storage Container!
  1. Make sure you're on your storage account page in the Azure portal.

  2. In the left menu for the storage account, scroll to the Data storage section, then select Containers.

  3. Select the + Container button.

  4. Type a name for your new container. The container name must be lowercase, must start with a letter or number, and can include only letters, numbers, and the dash (-) character.

  5. Set the level of public access to the container to "Blob (anonymous read access for blobs only)".

    image

  6. Select Create to create the container.


2: Creating an HTTP Trigger Function and Installing Required Packages:

Create a new HTTP trigger function named bunnimage-upload (no need to edit the code yet). Feel free to navigate to the previous issues/steps for guidance if you need extra help.

💡 Make sure to set the auth level as function for the HTTP trigger.

Before we start coding the trigger, we need to install the following npm packages/libraries in the Function App we created in the previous step:

  1. parse-multipart
  2. node-fetch@2
  3. @azure/storage-blob

Tip: To install a specific version of the Azure package run: npm i @azure/[email protected]

❓ How do I install `npm` packages?

Click on the "Console" tab in the left panel under "Development Tools".

https://user-images.githubusercontent.com/69332964/99189070-59e31d00-272d-11eb-80a4-17444e5fac65.png

Inside the console (shown on the right panel), type in the following commands:

npm init -y

npm install parse-multipart

npm install node-fetch@2

npm install @azure/storage-blob

📹 Walkthrough Video

walkthrough video

Week 4: One Cat is not enough Meow

Week 4 Step 6 ⬤⬤⬤⬤⬤⬤◯ | 🕐 Estimated completion: 5-20 minutes

One Cat isn't enough ~ Meow

Now that we've got a frontend that can only return one picture, let's take it to the next level and return FOUR pictures at the same time. To do this, let's edit our old twoCatz function.

✅ Task:

  • 1: Make sure you're on the twocatz-frontend branch
  • 2: Edit your twoCATZ HTTP Trigger from Week 1 so it takes in 4 parameters (name1, name2, name3, name4) and returns four pictures in base64 with the parameters.
  • 3: Remove the "random name" feature in your Azure Function
  • 4: Commit twocatz/index.js and test your result with Postman
  • 5: Create a pull request to merge twocatz-frontend onto main, and only merge the pull request when the bot approves your changes!

‼️ Requirements

  • Make sure you encode the pictures in base64
  • Make sure your parameters are named correctly!
  • The Function URL should still be in your TWOCATZ_ENDPOINT secret since you are editing the same function

🚧 Test your Work

Open up Postman again since we are testing an API.

⚠️ Make a GET request with the four name parameters. Do you get a JSON object containing name1, name2, name3, and name4 attributes encoded in base64?

💡 Having trouble? Look back to old branches - these skills were all used before!

📹 Walkthrough Video

walkthrough video

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.