├── - ├── .bit ├── .config ├── .info ├── config.yml ├── course-details.md ├── responses │ ├── 1-complete.md │ ├── 1.1-Week 1 Step 1.md │ ├── 1.2-Week 1 Step 2.md │ ├── 1.3-Week 1 Step 3.md │ ├── 1.4-Week 1 Step 4.md │ ├── 1.5-Week 1 Step 5.md │ ├── 1.6-Week 1 Step 6.md │ ├── 1.7-Week 1 Step 7.md │ ├── 1.8-Week 1 Step 8.md │ ├── 1.9-Week 1 Step 9.md │ ├── 2-complete.md │ ├── 2.1-Week 2 Step 1.md │ ├── 2.2-Week 2 Step 2.md │ ├── 2.3-Week 2 Step 3.md │ ├── 2.4-Week 2 Step 4.md │ ├── 2.5-Week 2 Step 5.md │ ├── 2.6-Week 2 Step 6.md │ ├── 2.7-Week 2 Step 7.md │ ├── 2.8-Week 2 Step 8.md │ ├── 2.9-Week 2 Step 9.md │ ├── 3-complete.md │ ├── 3.1-Week 3 Step 1.md │ ├── 3.2-Week 3 Step 2.md │ ├── 3.3-Week 3 Step 3.md │ ├── 3.4-Week 3 Step 4.md │ ├── 3.5-Week 3 Step 5.md │ ├── 3.6-Week 3 Step 6.md │ ├── 3.7-Week 3 Step 7.md │ ├── 3.8-Week 3 Step 8.md │ ├── 3.9-Week 3 Step 9.md │ ├── 4-complete.md │ ├── 4.1-Week 4 Step 1.md │ ├── 4.2-Week 4 Step 2.md │ ├── 4.3-Week 4 Step 3.md │ ├── 4.4-Week 4 Step 4.md │ ├── 4.5-Week 4 Step 5.md │ ├── 4.6-Week 4 Step 6.md │ ├── 4.7-Week 4 Step 7.md │ ├── 4.8-Week 4 Step 8.md │ ├── 5.1-Week 5 Step 1.md │ ├── 5.2-Week 5 Step 2.md │ ├── 5.3-Week 5 Step 3.md │ ├── 5.4-Week 5 Step 4.md │ ├── 5.5-Week 5 Step 5.md │ ├── 5.6-Week 5 Step 6.md │ ├── 5.7-Week 5 Step 7.md │ ├── bunnimage.md │ ├── feedback.md │ ├── finalproject.md │ ├── getting-emotional.md │ ├── hackervoice.md │ ├── hello.md │ ├── mergepr.md │ └── twocatz.md ├── scripts │ ├── commit.sh │ ├── create.py │ ├── getProgress.js │ ├── parse.py │ └── start.py ├── step-template.md ├── tests │ ├── cypress.json │ ├── cypress │ │ ├── fixtures │ │ │ └── testimage.jpg │ │ ├── integration │ │ │ ├── 4.1.spec.js │ │ │ ├── 4.2.spec.js │ │ │ ├── 4.3.spec.js │ │ │ ├── 4.4.spec.js │ │ │ ├── 4.5.spec.js │ │ │ ├── 4.7.spec.js │ │ │ └── cypress.json │ │ ├── screenshots │ │ │ └── 4.1.spec.js │ │ │ │ └── Testing Bunnimage -- Testing Week 4 Step 1 (failed).png │ │ └── videos │ │ │ └── 4.1.spec.js.mp4 │ ├── functions.js │ ├── sample-solutions │ │ ├── week1 │ │ │ ├── 1-2.helloworld.js │ │ │ ├── 1.1-blog.md │ │ │ ├── 1.4-hackervoice.js │ │ │ ├── 1.5-hackervoice.js │ │ │ ├── 1.6-twocatz.js │ │ │ ├── 1.7-twocatz.js │ │ │ └── 1.8-morse-code.js │ │ ├── week2 │ │ │ ├── 2.2-emotional.js │ │ │ ├── 2.3-emotional.js │ │ │ ├── 2.4-emotional.js │ │ │ ├── 2.5-emotional.js │ │ │ ├── 2.6-songrec.js │ │ │ ├── 2.7-songrec.js │ │ │ └── 2.8-songrec.js │ │ ├── week3 │ │ │ ├── 3.2-bunnimage.js │ │ │ ├── 3.3-bunnimage.js │ │ │ ├── 3.4-bunnimage.js │ │ │ ├── 3.5-bunnimage.js │ │ │ ├── 3.6-deepsecrets.js │ │ │ ├── 3.7-deepsecrets.js │ │ │ ├── 3.8-deepsecrets.js │ │ │ └── deepsecrets.js │ │ └── week4 │ │ │ ├── 4.1-bunnimage │ │ │ ├── index.html │ │ │ └── script.js │ │ │ ├── 4.2-bunnimage │ │ │ ├── index.html │ │ │ └── script.js │ │ │ ├── 4.3-bunnimage │ │ │ ├── index.html │ │ │ └── script.js │ │ │ ├── 4.4-bunnimage │ │ │ ├── index.html │ │ │ └── script.js │ │ │ ├── 4.5-twocatz │ │ │ ├── cat.jpg │ │ │ ├── index.html │ │ │ └── script.js │ │ │ ├── 4.6-twocatz │ │ │ └── twocatz.js │ │ │ └── 4.7-twocatz │ │ │ ├── cat.jpg │ │ │ ├── index.html │ │ │ └── script.js │ ├── spec.js │ ├── test.1.2.js │ ├── test.1.4.js │ ├── test.1.5.js │ ├── test.1.6.js │ ├── test.1.7.js │ ├── test.1.8.js │ ├── test.2.2.js │ ├── test.2.3.js │ ├── test.2.4.js │ ├── test.2.5.js │ ├── test.2.6.js │ ├── test.2.7.js │ ├── test.2.8.js │ ├── test.3.1.js │ ├── test.3.2.js │ ├── test.3.3.js │ ├── test.3.5.js │ ├── test.3.6.js │ ├── test.3.7.js │ ├── test.3.8.js │ ├── test.4.6.js │ ├── testimage.jpg │ └── wrongbranch.js └── workflows │ ├── bunnimage-frontend.yml │ ├── bunnimage.yml │ ├── deepsecrets.yml │ ├── emotionalgifs.yml │ ├── hackervoice.yml │ ├── hello.yml │ ├── morse.yml │ ├── song4u.yml │ ├── twocatz-frontend.yml │ ├── twocatz.yml │ └── week5.yml ├── .funcignore ├── .github ├── ISSUE_TEMPLATE │ ├── ---start-course.md │ ├── content-bug.md │ ├── feature-request.md │ ├── final-project.md │ └── test-bug.md ├── PULL_REQUEST_TEMPLATE.md ├── stale.yml └── workflows │ └── main.yml ├── .gitignore ├── .vscode └── extensions.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── GETTING_STARTED.md ├── LICENSE ├── README.md ├── blog.md ├── bunnimage └── index.html ├── checkup.sh ├── cypress.json ├── cypress ├── fixtures │ └── testimage.jpg └── integration │ ├── 4.1.spec.js │ ├── 4.2.spec.js │ ├── 4.3.spec.js │ ├── 4.4.spec.js │ ├── 4.5.spec.js │ ├── 4.7.spec.js │ └── cypress.json ├── host.json ├── package-lock.json ├── package.json ├── project ├── tech.md └── timeline.md ├── proxies.json └── setup.sh /-: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitprj/Intro-To-Serverless/7fd8024b8d259eb4772785db540bc0e01f34c3c1/- -------------------------------------------------------------------------------- /.bit/.config: -------------------------------------------------------------------------------- 1 | {'1.1-Week 1 Step 1.md': ['GitHub', '✅ Task:', ['blog.md'], 'PRmerge', 'n/a'], '1.2-Week 1 Step 2.md': ['Say "Hello" to VSCode', '✅ Task:', ['n/a'], 'checks', 'test.1.2.js'], '1.3-Week 1 Step 3.md': ['Getting Cat Pics with Postman 🐱', '✅ Task:', ['n/a'], 'IssueComment', 'n/a'], '1.4-Week 1 Step 4.md': ['Building our first function ⚡[HackerVoice]', 'Building our first function ⚡[HackerVoice]', ['n/a'], 'checks', 'test.1.4.js'], '1.5-Week 1 Step 5.md': ['Let me in!', '✅ Tasks:', ['n/a'], 'checks', 'test.1.5.js'], '1.6-Week 1 Step 6.md': ['Trying to make node-fetch happen', '✅ Task:', ['n/a'], 'checks', 'test.1.6.js'], '1.7-Week 1 Step 7.md': ['Name your Cat', '✅ Task:', ['n/a'], 'checks', 'test.1.7.js'], '1.8-Week 1 Step 8.md': ['[TOP SECRET] Morse Code Converter', '✅ Tasks:', ['n/a'], 'checks', 'test.1.8.js'], '1.9-Week 1 Step 9.md': ['📝 Week 1 Livestream Feedback', '📝 Week 1 Livestream Feedback', ['n/a'], 'feedback', 'n/a'], '2.1-Week 2 Step 1.md': ['Getting Emotional ~ With the Face API', '✅ Task:', ['n/a'], 'IssueComment', 'n/a'], '2.2-Week 2 Step 2.md': ['Getting Emotional ~ With Parse-Multipart', '✅ Task:', ['n/a'], 'checks', 'test.2.2.js'], '2.3-Week 2 Step 3.md': ['Getting Emotional ~ With the Face API', '✅ Task:', ['n/a'], 'checks', 'test.2.3.js'], '2.4-Week 2 Step 4.md': ['Getting Emotional ~ Returning the Dominant Emotion', '✅ Task:', ['n/a'], 'checks', 'test.2.4.js'], '2.5-Week 2 Step 5.md': ['Getting Emotional ~ Calling the Giphy API', '✅ Task:', ['n/a'], 'checks', 'test.2.5.js'], '2.6-Week 2 Step 6.md': ['Pose! Send a Pic to Functions', 'Checkpoint 2', ['n/a'], 'checks', 'test.2.6.js'], '2.7-Week 2 Step 7.md': ['How old are you??', 'Setting up the Twilio API', ['n/a'], 'checks', 'test.2.7.js'], '2.8-Week 2 Step 8.md': ['Hit me baby, one more time!', 'Connecting Locally to the Twillio API', ['n/a'], 'IssueComment', 'n/a'], '2.9-Week 2 Step 9.md': ['📝 Week 2 Livestream Feedback', '📝 Week 2 Livestream Feedback', ['n/a'], 'feedback', 'n/a'], '3.1-Week 3 Step 1.md': ['Week 3 Step 1', 'Learning Objectives', ['n/a'], 'IssueComment', 'n/a'], '3.2-Week 3 Step 2.md': ['Week 3 Step 2', 'Calling the Giphy API', ['index.js'], 'PRmerge', 'n/a'], '3.3-Week 3 Step 3.md': ['Week 3 Step 3', 'Sending the GIF', ['index.js'], 'PRmerge', 'n/a'], '3.4-Week 3 Step 4.md': ['Week 3 Step 4', 'Learning Objectives', ['n/a'], 'IssueComment', 'n/a'], '3.5-Week 3 Step 5.md': ['Week 3 Step 5', 'Setting Up Configuration', ['js/databaseContext.js'], 'checks', 'test.databaseContext.js'], '3.6-Week 3 Step 6.md': ['Week 3 Step 6', 'Modifying your Azure Function', ['n/a'], 'IssueComment', 'n/a'], '3.7-Week 3 Step 7.md': ['Week 3 Step 7', '📝 Week 3 Livestream Feedback', ['n/a'], 'feedback', 'n/a'], '4.1-Week 4 Step 1.md': ['Week 4 Step 1', 'Submitting Final Project Proposals', ['n/a'], 'IssueComment', 'n/a'], '4.2-Week 4 Step 2.md': ['Week 4 Step 2', 'Submitting an MVP', [''], '', ''], '4.3-Week 4 Step 3.md': ['Week 4 Step 3', 'Submitting the Final Project', [''], '', ''], '4.4-Week 4 Step 4.md': ['Week 4 Step 4', 'The Lightning Talk', [''], '', '']} -------------------------------------------------------------------------------- /.bit/.info: -------------------------------------------------------------------------------- 1 | {'responses': ['1.1-Week 1 Step 1.md', '1.2-Week 1 Step 2.md', '1.3-Week 1 Step 3.md', '1.4-Week 1 Step 4.md', '1.5-Week 1 Step 5.md', '1.6-Week 1 Step 6.md', '1.7-Week 1 Step 7.md', '1.8-Week 1 Step 8.md', '1.9-Week 1 Step 9.md', '2.1-Week 2 Step 1.md', '2.2-Week 2 Step 2.md', '2.3-Week 2 Step 3.md', '2.4-Week 2 Step 4.md', '2.5-Week 2 Step 5.md', '2.6-Week 2 Step 6.md', '2.7-Week 2 Step 7.md', '2.8-Week 2 Step 8.md', '2.9-Week 2 Step 9.md', '3.1-Week 3 Step 1.md', '3.2-Week 3 Step 2.md', '3.3-Week 3 Step 3.md', '3.4-Week 3 Step 4.md', '3.5-Week 3 Step 5.md', '3.6-Week 3 Step 6.md', '3.7-Week 3 Step 7.md', '4.1-Week 4 Step 1.md', '4.2-Week 4 Step 2.md', '4.3-Week 4 Step 3.md', '4.4-Week 4 Step 4.md'], 'weeks': ['1', '1', '1', '1', '1', '1', '1', '1', '1', '2', '2', '2', '2', '2', '2', '2', '2', '2', '3', '3', '3', '3', '3', '3', '3', '4', '4', '4', '4']} -------------------------------------------------------------------------------- /.bit/course-details.md: -------------------------------------------------------------------------------- 1 | # Sample Learning Lab 2 | 3 | *Course description: Place your course description here...* 4 | 5 | ## **Week 1** 6 | **Learning Objectives** 7 | - 8 | 9 | ## **Week 2** 10 | **Learning Objectives** 11 | - 12 | 13 | ## **Week 3** 14 | **Learning Objectives** 15 | - 16 | -------------------------------------------------------------------------------- /.bit/responses/1-complete.md: -------------------------------------------------------------------------------- 1 | That's it for Week 1, move on to Week 2 in your new issue! -------------------------------------------------------------------------------- /.bit/responses/1.5-Week 1 Step 5.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: checks 4 | scripts: test.1.5.js 5 | week: 1 6 | step: 5 7 | name: Week 1 Step 5 8 | --- 9 | Week 1 Step 5 ⬤⬤⬤⬤⬤◯◯◯◯ | 🕐 Estimated completion: 25-35 minutes 10 | 11 | # Let me in! 12 | 13 | ## ✅ Tasks: 14 | - [ ] Run `git pull origin [BRANCH_NAME]` 15 | - [ ] ***1:*** Add on to the HTTP Trigger in the previous issue to check if the user’s parameter input of “password” equals `letmein`. 16 | - If it does, output "Access granted." 17 | - If it doesn’t equal the correct password, output “Access denied.” 18 | - [ ] ***2:*** Commit the update function's code in a file named `hackervoice/index.js` to the root of the `hackervoice` branch! 19 | - [ ] ***3:*** Create a pull request to merges `hackervoice` to `main`, but do not merge unless the bot approves your changes. 20 | 21 | ## 🚧 Test your Work 22 | 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.“. 23 | 24 | ‼️ Make sure you follow the *exact* responses we provided. `Access denied.` and `Access granted.` 25 | 26 | ## 1: Return from a Function 27 | 28 | 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!** 29 | 30 | >💡 Recall the `context.res` object we saw in the HTTP Trigger template. 31 | 32 | ```js 33 | context.res = { 34 | // status: 200, /* Defaults to 200 */ 35 | body: your_response 36 | }; 37 | ``` 38 | 39 | Place your message to the user in `your_response`! 40 | ## 📹 Walkthrough Video 41 | [![walkthrough video](https://img.youtube.com/vi/rdAUUm3XwKE/0.jpg)](https://www.youtube.com/watch?v=rdAUUm3XwKE) -------------------------------------------------------------------------------- /.bit/responses/1.7-Week 1 Step 7.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: checks 4 | scripts: test.1.7.js 5 | week: 1 6 | step: 7 7 | name: Week 1 Step 7 8 | --- 9 | Week 1 Step 7 ⬤⬤⬤⬤⬤⬤⬤◯◯ | 🕐 Estimated completion: 5-15 minutes 10 | ## Name your Cat 11 | 12 | ## ✅ Task: 13 | - [ ] Run `git pull` 14 | - [ ] ***1:*** Modify your Azure Function to output TWO generated cat pictures from the cat API and TWO random names from list below: 15 | - "Shreya, Emily, Fifi, Beau, Evelyn, Julia, Daniel, Fardeen" 16 | - Use the CataaS API endpoint: https://cataas.com/cat/cute/says/Bitcamp 17 | - [ ] ***2:*** Return the images, encoded in **base64**, and names in **JSON format** in the body 18 | ```js 19 | body: { 20 | cat1: your-first-catpicture-in-base64, 21 | cat2: your-second-catpicture-in-base64, 22 | names: [name1, name2] 23 | } 24 | ``` 25 | - [ ] ***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! 26 | 27 | > 🚨 **NOTE:** If the CataaS API is not working, please use this alternate endpoint: https://bit-cat.azurewebsites.net/cat/says/serverless 28 | ## 🚧 Test your Work 29 | When you paste your **Function URL** in your browser or make a GET request with **Postman**, you might get something like: 30 | ```json 31 | { 32 | "cat1": "/9j/4AAQSk...", 33 | "cat2": "R0lGODlhCwHI...", 34 | "names": [ 35 | "Daniel", 36 | "Shreya" 37 | ] 38 | ``` 39 | 40 | ## 1: Select random items out of a list 41 | 42 | 1. Create an array with the names first 43 | 2. Generate a random number within the range of the array length 44 | 45 |
46 | ❓ How do I generate two random names? 47 |
48 | 49 | 1. Create an array with the names: 50 | ```js 51 | let names = ["name1", "name2"...] 52 | ``` 53 | 54 | 2. Generate a random value in the correct range: 55 | ```js 56 | let random_value = Math.floor(names.length * Math.random()) 57 | ``` 58 | 59 | 3. Get the name! 60 | ```js 61 | let resultname = names[random_value] 62 | ``` 63 | 64 | 4. Wrap the code for generating a random combination into a function that returns resultname and call the function twice to get two names! 65 | 66 |

67 |
68 | 69 | ## 2: Return images in JSON format 70 | 71 | `context.res` is the key to answering this question! 72 | 73 |
74 | ❓ How do I return the images using context.res? 75 |
76 | 77 | To return your two images and two names in the output: 78 | ```js 79 | context.res = { 80 | body: { 81 | cat1: your-first-catpicture-in-base64, 82 | cat2: your-second-catpicture-in-base64, 83 | names: [name1, name2] 84 | } 85 | } 86 | ``` 87 |
88 | 89 | ## 📹 Walkthrough Video 90 | [![walkthrough video](https://img.youtube.com/vi/WWbL61KjHDk/0.jpg)](https://www.youtube.com/watch?v=WWbL61KjHDk) -------------------------------------------------------------------------------- /.bit/responses/1.9-Week 1 Step 9.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: feedback 4 | scripts: n/a 5 | week: 1 6 | step: 9 7 | name: Week 1 Step 9 8 | --- 9 | ### 📝 Week 1 Video Feedback 10 | 11 | Please complete after you've watched the Week 1 walkthrough videos! If you haven't yet watched it but want to move on, just close this issue and **come back to it later.** 12 | 13 | ### Comment your feedback: 14 | 15 | Help us improve BitCamp Serverless - thank you for your feedback! Here are some questions you may want to answer: 16 | - How was the content? Did it help you? Was it too challenging or too easy? Did it help you complete the week's homework? 17 | - How was the pace? Was it hard to follow along? Did we go too slow? 18 | - If you could add/improve something to/in the videos, what would it be? 19 | 20 | ## **:camping: To move on, comment your feedback.** 21 | -------------------------------------------------------------------------------- /.bit/responses/2-complete.md: -------------------------------------------------------------------------------- 1 | That's it for Week 2, move on to Week 3 in your new issue! -------------------------------------------------------------------------------- /.bit/responses/2.8-Week 2 Step 8.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: checks 4 | scripts: test.2.8.js 5 | week: 2 6 | step: 8 7 | name: Week 2 Step 8 8 | --- 9 | 10 | Week 2 Step 8 ⬤⬤⬤⬤⬤⬤⬤⬤ | 🕐 Estimated completion: 5-10 minutes 11 | ## Ok, Boomer :neutral_face: 12 | ### ✅ Task: 13 | **Modify your Azure Function so that it texts the user back with a song.** 14 | - [ ] ***1:*** Retrieve song url from a JSON object 15 | ```js 16 | const songs = {"GenZ":"https://open.spotify.com/track/0SIAFU49FFHwR3QnT5Jx0k?si=1c12067c9f2b4fbf", 17 | "GenY":"https://open.spotify.com/track/1Je1IMUlBXcx1Fz0WE7oPT?si=a04bbdf6ec4948b9", 18 | "GenX":"https://open.spotify.com/track/4Zau4QvgyxWiWQ5KQrwL43?si=790d9e3ef2ed408d", 19 | "BabyBoomers":"https://open.spotify.com/track/4gphxUgq0JSFv2BCLhNDiE?si=1abb329f2dc24f50", 20 | "Unknown":"https://open.spotify.com/track/5ygDXis42ncn6kYG14lEVG?si=84b49b41d09d4d11"} 21 | ``` 22 | - [ ] ***2:*** Use `${}` to quickly insert strings 23 | - [ ] ***3:*** Return the url with a personalized message in the body of the request formatted like 24 | ``` 25 | We guessed you're part of this generation: [insert generation]! Happy listening! [song link]` 26 | ``` 27 | - [ ] ***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! 28 | 29 | ### 🚧 Test Your Work 30 | 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.** 31 | 32 | Example: 33 | ``` 34 | 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 35 | ``` 36 | 37 | ### Working with JSON 38 | JSON has **keys** and **values**. In our case, the generation is the **key**, and the url is the **value.** 39 | 40 | See if you can determine the url with this syntax: 41 | ```js 42 | let value = json_object[key] 43 | ``` 44 | 45 | ### Shortcuts with Strings 46 | You might be used to **concatenating strings and variables** like this: 47 | ```js 48 | let string = "Hello " + name 49 | ``` 50 | That can get tiring, though. Try this syntax: 51 | ```js 52 | let string = `Hello ${name}` 53 | ``` 54 | ## 📹 Walkthrough Video 55 | [![walkthrough video](https://img.youtube.com/vi/Tq8wxE6AcYg/0.jpg)](https://www.youtube.com/watch?v=Tq8wxE6AcYg) -------------------------------------------------------------------------------- /.bit/responses/2.9-Week 2 Step 9.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: feedback 4 | scripts: n/a 5 | week: 2 6 | step: 9 7 | name: Week 2 Step 9 8 | --- 9 | 10 | ### 📝 Week 2 Video Feedback 11 | 12 | Please complete after you've viewed the Week 2 walkthroughs! If you haven't yet watched it but want to move on, just close this issue and **come back to it later.** 13 | 14 | Help us improve BitCamp Serverless - thank you for your feedback! Here are some questions you may want to answer: 15 | - How was the content? Did it help you? Was it too challenging or too easy? Did it help you complete the week's homework? 16 | - How was the pace? Was it hard to follow along? Did we go too slow? 17 | - If you could add/improve something to/in the videos, what would it be? 18 | 19 | ## **:camping: To move on, comment your feedback.** -------------------------------------------------------------------------------- /.bit/responses/3-complete.md: -------------------------------------------------------------------------------- 1 | That's it for Week 3, move on to Week 4 in your new issue! -------------------------------------------------------------------------------- /.bit/responses/3.3-Week 3 Step 3.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: checks 4 | scripts: test.3.3.js 5 | week: 3 6 | step: 3 7 | name: Codename Blob 8 | --- 9 | 10 | Week 3 Step 3 ⬤⬤⬤◯◯◯◯◯◯ | 🕐 Estimated completion: 10-20 minutes 11 | 12 | # Codename Blob + 3RR0R Handling 13 | *This week, you will be going through steps to handle POST requests with no data.* 14 | 15 | ## ✅ Task: 16 | 17 | - [ ] ***1:*** Modify your function code so that it receives a header value called `codename` and uses it to name the image. 18 | - If an image isn't attached, return "Sorry! No image attached." 19 | - If an image is attached, return the output of the `uploadFile()` function 20 | - [ ] ***2:*** Commit your updated function code to `bunnimage/index.js` to the `bunnimage` branch. 21 | - [ ] ***3:*** Create a pull request that merges `bunnimage` onto `main`, and only merge the pull request when the bot approves your changes! 22 | 23 | ## 🚧 Test Your Work 24 | 25 | To test your work, use Postman to send a POST request *without* an image attached. You should see a response similar to the below: 26 | 27 | ```JSON 28 | { 29 | "body" : "Sorry! No image attached." 30 | } 31 | ``` 32 | > 💡 Yay! This means the error was successfully caught. 33 | 34 | ## 1: Modify the Function 35 | 36 | ### Handle Empty Data 37 | 38 | 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. 39 | 40 |
41 | ❓ How do I catch empty POST requests? 42 | 43 | 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! 44 | 45 | ```js 46 | let responseMessage = "" 47 | try { 48 | let password = // get the header called "codename" 49 | // use parse-multipart to parse the body 50 | // determine the file-type here! 51 | responseMessage = await uploadFile((place your parsedBody here), (place the extension here), (place the "codename" here)); 52 | // fill the parameters in! 53 | } catch(err) { 54 | context.log("Undefined body image"); 55 | responseMessage = "Sorry! No image attached." 56 | } 57 | ``` 58 | > 💡 Hint: `responseMessage` is what we're returning to the user as the output. 59 |
60 | 61 | > 💡 Hint: You'll need to add another parameter to the `uploadFile` function since it now has to receive the body, extension, AND filename! 62 | ### Headers 63 | Headers are yet another way to pass on information in HTTP requests. Here's how to access them: 64 | ```js 65 | let the_header_value = req.headers['insert_header_name']; 66 | ``` 67 | In this case, the header value will be used to name our picture. 68 | ## 📹 Walkthrough Video 69 | [![walkthrough video](https://img.youtube.com/vi/SX5Mzw3A-YI/0.jpg)](https://www.youtube.com/watch?v=SX5Mzw3A-YI) -------------------------------------------------------------------------------- /.bit/responses/3.8-Week 3 Step 8.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: checks 4 | scripts: test.3.8.js 5 | week: 3 6 | step: 8 7 | name: Exposed!! 8 | --- 9 | 10 | Week 3 Step 8 ⬤⬤⬤⬤⬤⬤⬤⬤◯ | 🕐 Estimated completion: 10-20 minutes 11 | 12 | # Exposed!! 13 | 14 | ## ✅ Task: 15 | Modify the `deepsecrets` Azure Function to 16 | - [ ] ***1:*** Query for all secrets stored in the CosmosDB 17 | - [ ] ***2:*** Select a random secret to return in the body 18 | - [ ] ***3:*** Commit your updated function code to `deepsecrets/index.js`to the `deepsecrets` branch 19 | - [ ] ***4:*** Create a pull request to merge `deepsecrets` onto `main`, and only merge the pull request when the bot approves your changes! 20 | 21 | ❗ Make sure to **return your message** in this format: 22 | ``` 23 | Thanks 😊! Stored your secret "insert_user's_message". 😯 Someone confessed that: "a_random_secret" 24 | ``` 25 | > 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. 26 | ## 🚧 Test Your Work 27 | 28 | 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! 29 | 30 | ``` 31 | Thanks 😊! Stored your secret "insert_user's_message". 😯 Someone confessed that: "a_random_secret" 32 | ``` 33 | 34 | > 💡 Yay! This means you've successfully queried for and returned a random secret. 35 | 36 | --- 37 | 38 | ## 1: Modifying the SQL Query 39 | 40 | In your `createDocument` function, you'll first want to modify the original SQL query to now query for *all* secrets inside your container. 41 | 42 |
43 | ❓ How do I select all secrets? 44 | 45 | ```js 46 | const querySpec = { 47 | query: "SELECT * from c" 48 | }; 49 | ``` 50 |
51 | 52 | ## 2: Selecting a Random Item 53 | > ⚠️ **Warning:** Keep in mind that *querying for ALL data* at the same time ***is not good practice.*** Data should only be queried on-demand when the user requires it. In this case, the user only needs *one random secret*, so in a "real-world application," we would utilize built-in functionalities for [MongoAPI](https://www.mongodb.com/docs/manual/reference/operator/aggregation/sample/) or [stored procedures](https://docs.microsoft.com/en-us/azure/cosmos-db/sql/how-to-write-stored-procedures-triggers-udfs?tabs=javascript#stored-procedures). The situation is a bit more tricky for SQL API; you can find more information in this [blog post](http://www.titov.net/2005/09/21/do-not-use-order-by-rand-or-how-to-get-random-rows-from-table/). 54 | 55 | Next, you'll want to select a random secret by: 56 | 57 | 1. Generating a random **integer** that is *strictly less* than the length of the array of secrets. (Hint: use `Math.floor()` and `Math.random()`) 58 | 2. Returning the secret at an index of this random number. 59 | 60 |
61 | ❓ How do I generate a random number for the index? 62 | 63 | The `Math.floor()` function returns the [floor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/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`. 64 | 65 | ```js 66 | let random_value = Math.floor(items.length * Math.random()); 67 | ``` 68 |
69 | 70 | 💡 Make sure to change your responseMessage to return the correct index of `items`. 71 | ## 📹 Walkthrough Video 72 | [![walkthrough video](https://img.youtube.com/vi/YBwaI0Ykv84/0.jpg)](https://www.youtube.com/watch?v=YBwaI0Ykv84) 73 | -------------------------------------------------------------------------------- /.bit/responses/3.9-Week 3 Step 9.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: feedback 4 | scripts: n/a 5 | week: 3 6 | step: 9 7 | name: Week 3 Step 9 8 | --- 9 | ### 📝 Week 3 Video Feedback 10 | 11 | Please complete after you've viewed the Week 3 walkthroughs! If you haven't yet watched it but want to move on, just close this issue and **come back to it later.** 12 | 13 | ### Comment your feedback: 14 | 15 | Help us improve BitCamp Serverless - thank you for your feedback! Here are some questions you may want to answer: 16 | - How was the content? Did it help you? Was it too challenging or too easy? Did it help you complete the week's homework? 17 | - How was the pace? Was it hard to follow along? Did we go too slow? 18 | - If you could add/improve something to/in the videos, what would it be? 19 | 20 | ## **:camping: To move on, comment your feedback.** 21 | -------------------------------------------------------------------------------- /.bit/responses/4-complete.md: -------------------------------------------------------------------------------- 1 | That's it for Week 4! Great job on finishing the course! -------------------------------------------------------------------------------- /.bit/responses/4.2-Week 4 Step 2.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: checks 4 | scripts: 4.2.spec.js 5 | week: 4 6 | step: 2 7 | name: Week 4 Step 2 8 | --- 9 | Week 4 Step 2 ⬤⬤◯◯◯◯◯ | 🕐 Estimated completion: 20-25 minutes 10 | 11 | ## Error Handling ~ Sir this is a Wendy's 12 | #### Demo: [🐰](https://week4step2.emilychen10.repl.co) 13 | *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.* 14 | 15 |

16 | 17 |

18 | 19 | ### ✅ Task: 20 | - [ ] ***1:*** Check you are on the `bunnimage-frontend` branch 21 | - [ ] ***2:*** Change your HTML form to accept `multipart/form-data` in `index.html` 22 | - [ ] ***3:*** Add a file input to `index.html` that accepts `image/x-png,image/jpeg` and add the attribute `name=image` 23 | - [ ] ***4:*** Modify the function in `index.js` to create an alert if either the file input or the `name` input is null 24 | - [ ] ***5:*** Modify the button so that it submits the form by changing its `type` attribute to `"submit"` 25 | - [ ] ***6:*** Prevent the page from reloading after submission 26 | - [ ] ***7:*** Commit your updated code to `bunnimage/index.html` and `bunnimage/script.js` 27 | - [ ] ***8:*** Create a pull request to merge `bunnimage` onto `main`, and only merge the pull request when the bot approves your changes! 28 | 29 | 30 | ### 🚧 Test your Work 31 | Open up your LiveServer plugin and start your server. 32 | 33 |
34 | ‼️ You have three test cases to try 35 |
36 | 37 | 1. **The "correct" way**: Submit both a file and text. Check that you receive "Thanks!" 38 | 2. **The "unexpected" way (file)**: Submit a file that is not png or jpeg. Does it work? 39 | 3. **The "unexpected" way (text input)**: Try submitting without entering a username. You should get an alert box that says "No name error." 40 |

41 |
42 | 43 | ## 1: Accepting Images as an Input 44 | 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` 45 | 46 | ```html 47 |

Bunnimage

48 |
49 | 50 | ``` 51 | 52 | To add the image upload input, add an additional file input within the form & change the submit button's `type`. 53 | 54 | ```html 55 | 56 | 57 | 58 | 59 | ``` 60 | 61 | ## 2: Input Validation? 62 | We need to validate two things. 63 | 64 |
65 | ❓ How do you check that the image is either .png or .jpeg format? 66 |
67 | 68 | The HTML `` 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 . 69 | 70 | ```html 71 | 72 | 73 | 74 | 75 | ``` 76 | 77 | 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. 78 | 79 | 1. Once a file is uploaded, you will need to display an error when a file of invalid format is added. 80 | 2. Access the selected file from the user on the `submit` event using a [DOM selector](https://developer.mozilla.org/en-US/docs/Web/API/File_API/Using_files_from_web_applications#:~:text=Accessing%20the%20first%20selected%20file%20using%20a%20classical%20DOM%20selector:). 81 | 3. Check if the file is either a PNG or JPEG image file using the [File API](https://developer.mozilla.org/en-US/docs/Web/API/File/type#:~:text=A%20string,%20containing%20the%20media%20type(MIME)) to see if the media type is "image/png" or "image/jpeg". 82 | 83 |

84 |
85 | 86 |
87 | ❓ How do you check that the text box isn't empty? 88 |
89 | 90 | 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.")` 91 | 92 | > :bulb: **Hint**: Use your JavaScript conditional skills! 93 |

94 |
95 | 96 | ## 📹 Walkthrough Video 97 | [![walkthrough video](https://img.youtube.com/vi/Jv2X4HuOD2U/0.jpg)](https://www.youtube.com/watch?v=Jv2X4HuOD2U) -------------------------------------------------------------------------------- /.bit/responses/4.4-Week 4 Step 4.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: checks 4 | scripts: 4.4.spec.js 5 | week: 4 6 | step: 4 7 | name: Week 4 Step 4 8 | --- 9 | 10 | Week 4 Step 4 ⬤⬤⬤⬤◯◯◯ | 🕐 Estimated completion: 5-20 minutes 11 | 12 | ## Downloading your image! 13 | #### Demo: [🐰](https://week4step4.emilychen10.repl.co/) 14 | *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.* 15 | 16 |

17 | 18 |

19 | 20 | ### ✅ Task: 21 | - [ ] ***1:*** Make sure you're on the `bunnimage-frontend` branch 22 | - [ ] ***2:*** Add an attribute of `id` with the value `button1` onto the upload button 23 | - [ ] ***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` 24 | - [ ] ***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 25 | - [ ] ***5:*** If the fetch was successful, open the file download link using [`window.open()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) and make sure it replaces the page instead of opening a new tab. 26 | - [ ] ***6:*** Commit your updated code to `bunnimage/index.html` and `bunnimage/script.js` 27 | - [ ] ***7:*** Create a pull request to merge `bunnimage-frontend` onto `main`, and only merge the pull request when the bot approves your changes! 28 | 29 | ### 🚧 Test your Work 30 | Open up your LiveServer plugin and start your local server. **To test your web app:** 31 | 32 | ⚠️ Type in the username for an image you have uploaded previously. Does the download button link to your file? 33 | 34 | ## 1: Opening a link 35 | 36 | [`window.open()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) is fairly straightforward to use. Here's the syntax: 37 | ```js 38 | window.open(URL, name, specs, replace) 39 | ``` 40 | ⭐ We will only be using `URL` and `name`. Use [this documentation](https://www.w3schools.com/jsref/met_win_open.asp) to fill it in. 41 | 42 | ## 📹 Walkthrough Video 43 | [![walkthrough video](https://img.youtube.com/vi/_9u9mwAKP3M/0.jpg)](https://www.youtube.com/watch?v=_9u9mwAKP3M) -------------------------------------------------------------------------------- /.bit/responses/4.5-Week 4 Step 5.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: checks 4 | scripts: 4.5.spec.js 5 | week: 4 6 | step: 5 7 | name: Week 4 Step 5 8 | --- 9 | 10 | Week 4 Step 5 ⬤⬤⬤⬤⬤◯◯ | 🕐 Estimated completion: 5-20 minutes 11 | 12 | ## Create the CataaS Website ~ meow 13 | 14 | #### Demo: [🐱](https://week4step5.emilychen10.repl.co/) 15 | *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)* 16 | 17 |

18 | 19 |

20 | 21 | ### ✅ Task: 22 | - [ ] ***1:*** Create a new `twocatz-frontend` branch 23 | - [ ] ***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` 24 | - [ ] ***3:*** Create a `img` object with id `image` in `catzapp/index.html` 25 | - [ ] ***4:*** Write the `y1k3s()` function that replaces the `img` div with a picture from the [CataaS API](http://cataas.com/) containing input from the text box 26 | - [ ] ***5:*** Commit your changes to `catzapp/index.html` and `catzapp/script.js` to the `twocatz-frontend` branch 27 | - [ ] ***6:*** Create a pull request that merges `twocatz-frontend` to `main`, and only merge the pull request when the bot approves your changes! 28 | 29 | 30 | > 💡 **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. 31 | 32 | > 🚨 **NOTE:** The CounselorBot will be checking the code to see if you use the [CataaS API](http://cataas.com/) endpoint. However, if the API is down, you may use the [substitute version](https://bit-cat.azurewebsites.net/cat/says/serverless) for testing purposes. Make sure to change it back when you submit your code! 33 | 34 | ### 🚧 Test your Work 35 | Open up your LiveServer plugin and start your local server. **To test your web app:** 36 | 37 | ⚠️ When you enter "catzrule" into the textbox and click the button, is there a picture that appears with a cat and "catzrule" on it? 38 | 39 | ## 1: Creating an HTML template 40 | In Visual Studio Code, you can create a html template containing the basic header tags by typing `!` + `Tab`. This will create something like: 41 | 42 | ```html 43 | 44 | 45 | 46 | 47 | 48 | 49 | Document 50 | 51 | 52 | 53 | 54 | 55 | ``` 56 | > Everything that appers on the page will be in the `` tags, and this is where you will be adding most (if not all of your html code. 57 | 58 | ## 2: Getting the Cat Pic 59 | The `img` tag embeds an image, and [it has attributes](https://www.w3schools.com/tags/tag_img.asp). We'll be using `src`. 60 | 61 |
62 | :question: How can I modify src to get the picture? 63 |
64 | 65 | When the button is clicked, it will call `y1k3s()`, so we will add a line of code in the function. 66 | In HTML, the img tag looks like this: 67 | ```html 68 | Girl in a jacket 69 | ``` 70 | 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. 71 | > 💡 Recall that the endpoint is https://cataas.com/cat/says/[your_text] 72 | 73 | ```js 74 | document.getElementById("YOUR_IMAGE_ID").src = THE_ENDPOINT + THE_INPUT 75 | ``` 76 | 77 |

78 |
79 | 80 | ## 📹 Walkthrough Video 81 | [![walkthrough video](https://img.youtube.com/vi/cLq1TcBMGHE/0.jpg)](https://www.youtube.com/watch?v=cLq1TcBMGHE) -------------------------------------------------------------------------------- /.bit/responses/4.6-Week 4 Step 6.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: checks 4 | scripts: 4.6.spec.js 5 | week: 4 6 | step: 6 7 | name: Week 4 Step 6 8 | --- 9 | 10 | Week 4 Step 6 ⬤⬤⬤⬤⬤⬤◯ | 🕐 Estimated completion: 5-20 minutes 11 | 12 | ## One Cat isn't enough ~ Meow 13 | 14 | *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.* 15 | 16 | ### ✅ Task: 17 | - [ ] ***1:*** Make sure you're on the `twocatz-frontend` branch 18 | - [ ] ***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. 19 | - [ ] ***3:*** Remove the "random name" feature in your Azure Function 20 | - [ ] ***4:*** Commit `twocatz/index.js` and test your result with Postman 21 | - [ ] ***5:*** Create a pull request to merge `twocatz-frontend` onto `main`, and only merge the pull request when the bot approves your changes! 22 | 23 | ### ‼️ Requirements 24 | - Make sure you encode the pictures in **base64** 25 | - Make sure your parameters are named correctly! 26 | - The Function URL should still be in your `TWOCATZ_ENDPOINT` secret since you are **editing the same function** 27 | 28 | ### 🚧 Test your Work 29 | Open up **Postman** again since we are testing an API. 30 | 31 | ⚠️ 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? 32 | 33 | > :bulb: Having trouble? Look back to old branches - these skills were all used before! 34 | ## 📹 Walkthrough Video 35 | [![walkthrough video](https://img.youtube.com/vi/pRjehHQI_LE/0.jpg)](https://www.youtube.com/watch?v=pRjehHQI_LE) -------------------------------------------------------------------------------- /.bit/responses/4.7-Week 4 Step 7.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: checks 4 | scripts: 4.7.spec.js 5 | week: 4 6 | step: 7 7 | name: Week 4 Step 7 8 | --- 9 | 10 | Week 4 Step 7 ⬤⬤⬤⬤⬤⬤⬤ | 🕐 Estimated completion: 5-20 minutes 11 | 12 | ## Ultimate CAT INVASION! ~ 13 | #### Demo: [🐱](https://week4step7.emilychen10.repl.co/) 14 | *It's now time to connect everything together! (catzapp/index.html, catzapp/script.js, and your Azure function)* 15 | 16 |

17 | 18 |

19 | 20 | ### ✅ Task: 21 | - [ ] ***1:*** Make sure you're on the `twocatz-frontend` branch 22 | - [ ] ***2:*** Create 4 input boxes to accept 4 names, each with an `id` of `name1`, `name2`, `name3`, and `name4` respectively 23 | - [ ] ***3:*** Modify `catzapp/script.js` so that it calls your Azure Function with the 4 names as parameters 24 | - [ ] ***4:*** Create 4 `img` tags, each with an `id` of `image1`, `image2`, `image3`, and `image4` respectively 25 | - [ ] ***5:*** Display the outputs of the Azure Function as images in the tags 26 | - [ ] ***6:*** Commit your updated changes to `catzapp/index.html` and `catzapp/script.js` 27 | - [ ] ***7:*** Create a pull request to merge `twocatz-frontend` onto `main`, and only merge the pull request when the bot approves your changes! 28 | 29 | 30 | > :bulb: 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](https://github.com/bitprj/Intro-To-Serverless/blob/main/GETTING_STARTED.md#question-do-i-have-to-push-a-commit-to-run-a-check). 31 | 32 | ### 🚧 Test your Work 33 | Open up your LiveServer plugin and start your local server. **To test your web app:** 34 | 35 | ⚠️ When you enter FOUR NAMES into each of the text boxes, do FOUR CAT PICTURES show up below? 36 | 37 | ## 1: How can Base64 turn into a Cat? 38 | Now that you should've received strings of `base64` from your Azure function call, you need some way to display them for the users. 39 | 40 |
41 | :question: How can I display base64? 42 |
43 | 44 | 1. Retrieve the base64 values from your API 45 | 2. Append `data:image/png;base64,` in front of the base64 data 46 | 3. Like you've done previously, modify the `src` attribute of the image tags and set it equal to the string you created in :two: 47 | 48 | Read more [here](https://www.w3docs.com/snippets/html/how-to-display-base64-images-in-html.html) 49 |

50 |
51 | 52 | ## 📹 Walkthrough Video 53 | [![walkthrough video](https://img.youtube.com/vi/m2wGFx-WCZg/0.jpg)](https://www.youtube.com/watch?v=m2wGFx-WCZg) -------------------------------------------------------------------------------- /.bit/responses/4.8-Week 4 Step 8.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: feedback 4 | scripts: n/a 5 | week: 4 6 | step: 8 7 | name: Week 4 Step 8 8 | --- 9 | ### 📝 Week 4 Video Feedback 10 | 11 | Please complete after you've viewed the Week 3 walkthroughs! If you haven't yet watched it but want to move on, just close this issue and **come back to it later.** 12 | 13 | ### Comment your feedback: 14 | 15 | Help us improve BitCamp Serverless - thank you for your feedback! Here are some questions you may want to answer: 16 | - How was the content? Did it help you? Was it too challenging or too easy? Did it help you complete the week's homework? 17 | - How was the pace? Was it hard to follow along? Did we go too slow? 18 | - If you could add/improve something to/in the videos, what would it be? 19 | 20 | ## **:camping: To move on, comment your feedback.** 21 | -------------------------------------------------------------------------------- /.bit/responses/5.1-Week 5 Step 1.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: PRmerge 4 | scripts: n/a 5 | week: 5 6 | step: 1 7 | name: Final Project Step 1 8 | --- 9 | Final Project Step 1 ⬤◯◯◯◯◯ | 🕐 Estimated completion: 1-2 hours 10 | 11 | ## Laying out the groundwork 12 | 13 | ## ✅ Task: 14 | - [ ] Create a new branch called `final-project` 15 | - Commit all of your final project related changes in the `final-project` branch 16 | - [ ] Follow the template in "project/tech.md" file and plan out your project (directly edit the file) 17 | - Keep all of your final project related files in the "project" folder 18 | - [ ] 🚀 Commit your changes to the `final-project` branch, make a pull request, and merge it 19 | 20 | ## Planning it out 21 | 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 22 | 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 23 | 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 24 | 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 25 | 26 | ### Preparing a Checklist 27 | 28 | 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. 29 | 30 |
31 | 32 | 33 | > this you? 34 | 35 | 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. 36 | 37 | ### Researching APIs (and other technologies) 38 | 39 | 40 | 41 | 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. 42 | 43 | #### How to find the right API? 44 | 45 | 1. Start with a simple Google search or filter through online lists such as [rapidapi.com](rapidapi.com) if you already know what purpose you want your API to serve (e.g. find a location, send an SMS, ...) 46 | 2. Research lists such as [rapidapi.com](https://rapidapi.com/categories) that have tags/categories that you can filter through to get some ideas if you're looking for 💡 project inspiration 💡 through cool new APIs 47 | 3. Talk to people in the industry or try watching some API workshops to see what is out there 48 | 49 | #### Popular API categories 50 | 51 | - SMS notifications (e.g. Twilio API) 52 | - Location based APIs 53 | - Food related APIs 54 | - Weather APIs 55 | 56 | #### Examples of projects and which APIs they used 57 | 58 | - [Dine the Distance](https://bitproject.org/blog/serverless-dine-the-distance) uses the Azure Maps API to get restaurant recommendations 59 | - [Face Mask Detection](https://bitproject.org/blog/serverless-face-mask-detection) Tensorflow API for machine learning 60 | - Flux uses the Power Grid API 61 | 62 | 63 | 64 | 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! -------------------------------------------------------------------------------- /.bit/responses/5.2-Week 5 Step 2.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: PRmerge 4 | scripts: n/a 5 | week: 5 6 | step: 1 7 | name: Final Project Step 2 8 | --- 9 | Final Project Step 2 ⬤⬤◯◯◯◯ | 🕐 Estimated completion: 30 minutes 10 | 11 | ## Laying out the groundwork 12 | 13 | ### ✅ Task: 14 | - [ ] Make sure you're on the `final-project` branch 15 | - [ ] Create a flowchart that demonstrates how different components in your project interact with each other 16 | - Create a new file on [figma.com](figma.com) or [whimsical.com](whimsical.com) called "Serverless Camp Project" 17 | - Include any Azure Functions and APIs you plan on using 18 | - [ ] 🚀 Upload the flowchart as a jpg/png file in the "project" folder 19 | - [ ] 🚀 Commit your changes to the `final-project` branch, make a pull request, and merge it 20 | 21 | 22 | 23 | ## Mapping it out 24 | 25 | 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. 26 | 27 | ### Purpose of Programming Flowcharts 28 | 29 | - Plan before the development phase - this helps you save a lot of hours of work 30 | - Document your project and communicate how it works to others (this is really valuable when someone else is helping you debug your code) 31 | - Prevent major technical issues with your code before you event start 32 | - Ensure that you are more effectively using all the technologies available to you 33 | - You will need to refer back to the flowchart while developing to review your work and sometimes come up with new ideas 34 | 35 | ### Converting the checklist into a flowchart 36 | 1. **Create a shape** for each of the components from your checklist and **label them** inside their designated shape 37 | 2. **Draw lines** on all of the technologies that interact with each other 38 | 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) 39 | 4. Use **arrow heads** to show the direction in which one technology is impacting another 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /.bit/responses/5.3-Week 5 Step 3.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: PRmerge 4 | scripts: n/a 5 | week: 5 6 | step: 3 7 | name: Final Project Step 3 8 | --- 9 | Final Project Step 3 ⬤⬤⬤◯◯◯ | 🕐 Estimated completion: 20-40 minutes 10 | 11 | ## Reviewing your Flowchart 12 | 13 | ## ✅ Task: 14 | 15 | - [ ] 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 16 | - [ ] 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 17 | - [ ] Use the feedback you improve your flowchart and update the file in the `final-project` branch 18 | - [ ] 🚀 Commit your changes to the `final-project` branch, make a pull request, and merge it 19 | - In the pull request description, explain the feedback you received and the changes you made 20 | 21 | ## How to improve a flowchart 22 | There are 3 main things to focus on when you are getting feedback on and improving your flowchart: 23 | 24 | #### :one: Logic: 25 | - Does the flowchart make sense? 26 | - Do the arrow directions makes sense? 27 | - Are the descriptions for the interactions between technologies logically correct? 28 | #### :two: Accuracy: 29 | - Did you include all components required for this project? 30 | - Do the arrows represent accurate relationships? 31 | - Are the descriptions accurate to what purposes the components serve? 32 | #### :three: Readibility and visual appeal: 33 | - Is it easy to understand what is going on? Is there an easier way to explain it? 34 | - Are the colors/shapes simple and easy to read? 35 | - If you are using color codes, do you have a legend that describes what the colors mean? 36 | - Does the flowchart have a title? 37 | 38 | 39 | -------------------------------------------------------------------------------- /.bit/responses/5.4-Week 5 Step 4.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: PRmerge 4 | scripts: n/a 5 | week: 5 6 | step: 4 7 | name: Final Project Step 4 8 | --- 9 | Final Project Step 4 ⬤⬤⬤⬤◯◯ | 🕐 Estimated completion: 25 minutes 10 | 11 | ## Timeline 12 | 13 | ## ✅ Task: 14 | 15 | - [ ] Follow the template in "project/timeline.md" file and develop a draft of your timeline 16 | - [ ] Ask a mentor (or someone with software engineering experience) to review your gameplan 17 | - [ ] Once your timeline is mentor-approved, convert your timeline into issues in your project repo 18 | - Use the Final Project Issue Template 19 | - [ ] 🚀 Commit your changes to the `final-project` branch, make a pull request, and merge it 20 | 21 | ## Developing a timeline 22 | 23 | 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.** 24 | 25 | ### :question: Where to start 26 | 27 | Look back at your checklist and decide where you want to start developing your project. Do you want to start with: 28 | - UI of the app/webapp 29 | - Azure function 30 | - API implementation 31 | 32 | The best approach recommended by our mentors is to "start small then build big." If you're really unsure, ask your mentor! 33 | 34 | ### 🥧 Break it down 35 | 36 | 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. 37 | 38 | In reality, they might end up taking more time (especially with debugging involved) but try your best to estimate. 39 | 40 | ### ⏰ Scheduling deadlines 41 | 42 | #### Speak with your mentor to make sure you can develop your timeline to maximize your timeline. 43 | > 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? 44 | 45 | #### Sometimes you might become lucky and end up having extra time on your hands. 46 | > How do you plan on maximizing the impact of this time so you can ship the best version of your project at the end? 47 | 48 | Be clearn on how to manage your own time and hold yourself accountable! Ask your peers and friends for help if you need it. 49 | 50 | ### 📢 Getting feedback 51 | 52 | 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. 53 | 54 | ### 🔄 Converting tasks into issues 55 | 56 | 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. 57 | 58 | As issue is like a task that you need to work on for your code. Here are some examples: 59 | - Develop function to store image in database 60 | - Convert image to base64 61 | - Fix bug on like 29 in Azure function 62 | - Document function send_tweets() in file app.js 63 | 64 |
65 | :exclamation: How do I create a new issue? 66 |
67 | 68 | Screen Shot 2021-06-03 at 1 32 11 PM 69 | 70 | 1. At the top menu, click on Issues. 71 | 2. Click on the green button that called "New Issue" 72 | 3. In the title, write a brief description of what needs to be done (look at the list above) 73 | 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 74 | 5. Click on the green "Submit new isuse" button 75 |

76 |
77 | 78 |
79 | :exclamation: How should I format my issue? 80 |
81 | 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. 82 | 83 | ``` 84 | ### [Task Name]: 85 | 86 | #### Description 87 | - [Replace with description] 88 | 89 | #### ETA: 90 | > How long do you think it will take to complete this? 91 | - [Replace with eta] 92 | 93 | #### Objective: 94 | > Checklist of everything you need to do to complete this issue 95 | - [ ] [Replace with small task 1] 96 | - [ ] [Replace with small task 2] 97 | - [ ] [Replace with small task 3] 98 | ``` 99 |

100 |
101 | -------------------------------------------------------------------------------- /.bit/responses/5.5-Week 5 Step 5.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: PRmerge 4 | scripts: n/a 5 | week: 5 6 | step: 5 7 | name: Final Project Step 5 8 | --- 9 | Final Project Step 5 ⬤⬤⬤⬤⬤◯ | 🕐 Estimated completion: 1-5 hours 10 | 11 | ## Start Coding! 12 | 13 | ## ✅ Task: 14 | 15 | - [ ] Based on your schedule determined in "timeline.md", find the first issue you need to work on and begin your first task 16 | - [ ] 🚀 Close the issue when you have completed 17 | - [ ] 🚀 Commit your changes to the `final-project` branch, make a pull request, and merge it 18 | 19 | ### Developing the first component 20 | 21 | > :bulb: Start small then build big! 22 | 23 | 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. 24 | * Begin by just having one working component of the project. Ex: 25 | * Press a button on your webpage and receive information 26 | * Create a working Azure Function that can return information 27 | * After that's complete and tested, begin building onto that with more and more features! 28 | 29 | ### Committing code 30 | 31 | :exclamation: Best practices for commiting code is to **commit it constantly**. Any time you make a change, commit the change. 32 | 33 | #### :question: Why? 34 | 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. 35 | 36 | > :bulb: **Tip:**: You want to name the commit accurately to the changes you have made so that they are easy to search through later. 37 | -------------------------------------------------------------------------------- /.bit/responses/5.6-Week 5 Step 6.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: PRmerge 4 | scripts: n/a 5 | week: 5 6 | step: 6 7 | name: Final Project Step 6 8 | --- 9 | Final Project Step 6 ⬤⬤⬤⬤⬤⬤ | 🕐 Estimated completion: 25 minutes 10 | 11 | ## Documentation 12 | 13 | ### ✅ Task: 14 | 15 | - [ ] Document your first component in blog.md 16 | - [ ] 🚀 Commit your changes to the `final-project` branch, make a pull request, and merge it 17 | 18 | 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. 19 | 20 | > Check out the [Save Money and Frustration on Amazon using Azure Functions](https://bitproject.org/blog/serverless-price-scraper) blog to see how Fifi broke down her project into multiple steps. 21 | 22 | Screen Shot 2021-06-05 at 1 03 19 PM 23 | 24 | > Check out the [Creating a File Sharing and Conversion Web App with Azure Functions](https://bitproject.org/blog/serverless-bunnimage) 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. 25 | 26 | Screen Shot 2021-06-05 at 1 00 40 PM 27 | 28 | ### Quick tips 29 | 30 | * If your project required any kind of groundwork like installing npm packages or setting up Azure locally, document this first in "Step 0" 31 | * Break down your code: 32 | * Copy all of your code and paste it into a code snippet in your blog 33 | * Break it down into multiple snippets in order of how you wrote the code 34 | * Before each snippet, write a description of what the code is for and how it works 35 | * Define keywords that were new to you or would be to your audience 36 | * Tip: use substeps to make your blog easier to follow 37 | 38 | If you have completed multiple components of your project this week, document them as you go. 39 | 40 | ### Documentation Best Practices: 41 | 42 |
43 | :exclamation: How do I document my code? 44 |
45 | 46 | Documentation is easier than it seems! Here are some tips to consider when you begin: 47 | 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. 48 | 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. 49 | 3. If there are concepts/terms that seem complicated, try your best to explain them in a way that would make sense to you. 50 | 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. 51 | 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. 52 | 53 |

54 |
-------------------------------------------------------------------------------- /.bit/responses/5.7-Week 5 Step 7.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: PRmerge 4 | scripts: n/a 5 | week: 6 6 | step: 1 7 | name: Final Project Step 7 8 | --- 9 | Final Project Step 7 ⬤◯◯◯◯◯ | 🕐 Estimated completion: 30 mins 10 | 11 | ## Continuing your work... 12 | 13 | ## ✅ Task: 14 | - [ ] Continue working in the `final-project` branch 15 | - [ ] Review all of the issues that are currently open: 16 | - Close the issues that you have completed 17 | - Add 3-4 more issues based on the work you plan on doing this week (if they are not already there) 18 | - Label the issues you want to work on as "Priority" 19 | - Label the issues that you need your mentor's help with as "mentor" and send them to your mentor to review before your meeting 20 | - [ ] 🚀 Commit any changes to the `final-project` branch, make a pull request, and merge it 21 | -------------------------------------------------------------------------------- /.bit/responses/bunnimage.md: -------------------------------------------------------------------------------- 1 | ## 🐰 Bunnimage: Complete! 2 | 3 | Hop on over to the next issue. 4 | 5 | ![bunnies](https://media.giphy.com/media/wsJHi6a1JwoXC/giphy.gif) -------------------------------------------------------------------------------- /.bit/responses/feedback.md: -------------------------------------------------------------------------------- 1 | ## Providing Feedback 2 | 3 | What was confusing about this week? If you could change or add something to this week, what would you do? Your feedback is valued and appreciated! -------------------------------------------------------------------------------- /.bit/responses/finalproject.md: -------------------------------------------------------------------------------- 1 | # Good luck! :wave: 2 | 3 | ### ❗ Uninstall the `CounselorBot` 4 | 5 |
6 | :question: How do I do that? 7 |
8 | 9 | First, go to `Settings` and click on `Integrations`. 10 | ![Screen Shot 2021-06-20 at 9 51 02 PM](https://user-images.githubusercontent.com/69332964/122696810-fd3f6780-d211-11eb-89f7-742f9e82555d.png) 11 | 12 | Next, find the CounselorBot and click on `Configure` 13 | ![image](https://user-images.githubusercontent.com/69332964/122696950-4ee7f200-d212-11eb-9637-050e0d007cd8.png) 14 | 15 | Finally, click on `Uninstall` and you should be good to go! 16 | ![image](https://user-images.githubusercontent.com/69332964/122697025-776fec00-d212-11eb-97bf-112289cbe34d.png) 17 | 18 |
19 | 20 | ### ❔ What's next? 21 | We'll still be around, but just keep following these basic steps until you reach the final version of your final project: 22 | 23 | - [ ] Continue working in the `final-project` branch 24 | - [ ] Following what we did last week, select the next issue you want to work on and develop your project 25 | - [ ] Document the project in `blog.md` 26 | - [ ] Close the issue when you are satisfied with your updates 27 | - [ ] 🚀 Commit any changes to the `final-project` branch, make a pull request, and merge it 28 | 29 | ![maytheforcebewithyou](https://media.giphy.com/media/l3fZMzs7sIisUt0Tm/giphy.gif) 30 | -------------------------------------------------------------------------------- /.bit/responses/getting-emotional.md: -------------------------------------------------------------------------------- 1 | ## 🎭 Getting Emotional ~ Complete! 2 | 3 | Pat yourself on the back. 4 | 5 | ![celebrating](https://media.giphy.com/media/IwAZ6dvvvaTtdI8SD5/giphy.gif) -------------------------------------------------------------------------------- /.bit/responses/hackervoice.md: -------------------------------------------------------------------------------- 1 | # 🕵🏾‍♀️ N1c3 J0b H4ck3r! 2 | 3 | Are you ready for some cats? (We hope you're not allergic) 4 | 5 | ![hackerman](https://media.giphy.com/media/MM0Jrc8BHKx3y/giphy.gif) -------------------------------------------------------------------------------- /.bit/responses/hello.md: -------------------------------------------------------------------------------- 1 | # ⚡️ You're ready for Serverless! 2 | 3 | Move on to the next issue. 4 | 5 | ![moveon](https://media.giphy.com/media/xXflsa7VrvT7gGzNFN/giphy.gif) -------------------------------------------------------------------------------- /.bit/responses/mergepr.md: -------------------------------------------------------------------------------- 1 | # ⏰ Time to merge! 2 | 3 | Go ahead and merge this branch to `main` to move on. Great work finishing this section! 4 | 5 | ![merge](https://media.giphy.com/media/tODygE8KCqBzy/giphy.gif) 6 | 7 | > ⚠️ If you receive a `Conflicts` error, simply press `Resolve conflicts` and you should be good to merge! 8 | -------------------------------------------------------------------------------- /.bit/responses/twocatz.md: -------------------------------------------------------------------------------- 1 | # 👋 Bye! See you in Week 2. 2 | 3 | ![cat](https://media.giphy.com/media/cBncDNrdxga2I/giphy.gif) -------------------------------------------------------------------------------- /.bit/scripts/commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | git add . 4 | git commit -m "Syncing course files" 5 | git push origin main 6 | -------------------------------------------------------------------------------- /.bit/scripts/getProgress.js: -------------------------------------------------------------------------------- 1 | const args = require('minimist')(process.argv.slice(2)) 2 | const fetch = require('node-fetch'); 3 | 4 | async function main() { 5 | let step; 6 | const owner = args['owner']; 7 | const repo = args['repo']; 8 | 9 | const options = { 10 | headers: { 11 | repo, 12 | owner 13 | } 14 | } 15 | 16 | try { 17 | const resp = await fetch('https://counselorbot.azurewebsites.net/api/hasuraCountQuery?code=dWJdQz4o2bEoGesnmZDi9oi8/v7xk8NaVEU9ykgxC1xLPrCeAkd96A==', options); 18 | const data = await resp.json(); 19 | step = data.step.data.users_progress[0].count; 20 | 21 | /* 22 | { 23 | "step": { 24 | "data": { 25 | "users_progress": [ 26 | { 27 | "count": 1 28 | }, 29 | { 30 | "count": 0 31 | } 32 | ] 33 | } 34 | } 35 | } 36 | */ 37 | } 38 | catch (e) { 39 | step = "[ERROR] Something went wrong! ⚠️" 40 | } 41 | 42 | console.log(step) 43 | 44 | } 45 | 46 | main(); 47 | 48 | -------------------------------------------------------------------------------- /.bit/scripts/parse.py: -------------------------------------------------------------------------------- 1 | import re 2 | import ast 3 | 4 | weeks = [] 5 | responses = [] 6 | steps = {} 7 | 8 | with open(".bit/.info", "r") as myfile: 9 | readList = ast.literal_eval(myfile.read()) 10 | print(readList) 11 | responses = readList["responses"] 12 | weeks = readList["weeks"] 13 | nweeks = int(max(weeks)) 14 | print("Number of weeks:", nweeks) 15 | 16 | # determining number of steps per week 17 | for i in range(1,nweeks+1): 18 | count = 0 19 | for file in responses: 20 | if int(file[0]) == i: 21 | count += 1 22 | steps[i] = count 23 | 24 | print("Number of steps each week:", steps) 25 | 26 | try: 27 | # determining course name and description 28 | name = "# (.*)" 29 | content = "(?<=\*)[^*]+(?=\*)" 30 | with open(".bit/course-details.md", "r") as file: 31 | file = file.read() 32 | course_name = re.findall(name, file)[0] 33 | course_descr = re.findall(content, file)[0] 34 | except: 35 | raise Exception("-------------------------\n[WARNING] Syncing was not successful. Please check your 'course-details.md' file and make sure it adheres to the specified format.\n-------------------------") 36 | 37 | print("Course name: " + course_name + "\n" + "Course description: " + course_descr) 38 | 39 | try: 40 | # determining step names and descriptions 41 | stepContent = {} 42 | for i in responses: 43 | with open(".bit/responses/" + i, "r") as file: 44 | title = "## (.*)" 45 | des = "### (.*)" 46 | move = "files: (.*)" 47 | stepType = "stepType: (.*)" 48 | scripts = "scripts: (.*)" 49 | file = file.read() 50 | step_name = re.findall(title, file)[0] 51 | step_descr = re.findall(des, file)[0] 52 | step_move = re.findall(move, file)[0].split(", ") 53 | step_type = re.findall(stepType, file)[0] 54 | step_scripts = re.findall(scripts, file)[0] 55 | stepContent[i] = [step_name, step_descr, step_move, step_type, step_scripts] 56 | 57 | print("Step data: ", stepContent) 58 | 59 | with open(".bit/.config", "w+") as myfile: 60 | myfile.write(str(stepContent)) 61 | except: 62 | raise Exception("-------------------------\n[WARNING] Syncing was not successful. Please check your response files and ensure that the content adheres with the specified format.\n-------------------------") 63 | -------------------------------------------------------------------------------- /.bit/scripts/start.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | responses = sorted(os.listdir(".bit/responses")) 4 | 5 | total = {} 6 | weeks = [] 7 | rm = [] 8 | 9 | try: 10 | # determining number of weeks 11 | for file in responses: 12 | if file[-12:] == "-complete.md" and len(file) <= 14: 13 | rm.append(file) 14 | elif file[0].isnumeric(): 15 | weeks.append(file[0]) 16 | else: 17 | rm.append(file) 18 | 19 | for delete in rm: 20 | responses.remove(delete) 21 | 22 | with open(".bit/.info", "w+") as myfile: 23 | total["responses"] = responses 24 | total["weeks"] = weeks 25 | print(total) 26 | myfile.write(str(total)) 27 | 28 | except: 29 | raise Exception("-------------------------\n[ERROR] Syncing was not successful. Please check your filenames and ensure that they adhere to the specified format.\n-------------------------") -------------------------------------------------------------------------------- /.bit/step-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | files: n/a 3 | stepType: PRmerge, IssueComment, checks, or feedback? 4 | scripts: n/a 5 | week: x 6 | step: x 7 | name: Week x Step x 8 | --- 9 | 10 | Week x Step x ⬤⬤◯◯◯◯◯◯ | 🕐 Estimated completion: 5-20 minutes 11 | 12 | # Hi! This is the step title 13 | > What is this step about? What will the student do? 14 | 15 | ## ✅ Task: 16 | - [ ] Create a new branch called `xx` 17 | - [ ] ***1:*** Step 1 (be descriptive) 18 | - [ ] ***2:*** Step 2 19 | - [ ] 🚀 Place your function URL in a [repository secret](https://docs.github.com/en/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository) called `xx_ENDPOINT` and commit your code to `xx/index.js` on your `xx` branch 20 | - [ ] 🚀 Create a pull request that merges `xx` to `main`, but **do not merge it** 21 | 22 | ## 🚧 Test your Work 23 | How should the student test? 24 | 25 |
26 | :white_check_mark: Expected Output 27 | 28 | Output here. 29 |
30 | 31 | ## 1. Step 1 32 | 33 | ### Breaking it down 34 | Fill this in! Don't forget screenshots! 35 | 36 | > 💡 Hint or an AHA moment 37 | 38 | What `will you put` here? 39 | 40 |
41 | 42 | :exclamation: Here's an explanation! 43 | 44 | 45 | ``` 46 | context.log("Hi, put some code here!") 47 | ``` 48 | Explain in depth. 49 |
50 | 51 | 52 |
53 | :question: Answer a question? 54 |
55 | 56 | Answer the question here. Don't forget screenshots! 57 |
58 | 59 | ## 2. Step 2 60 | 61 | Some steps for the student to take: 62 | 1. One 63 | 2. Two 64 | 3. Three 65 | 66 |
67 | :question: Answer another question? 68 | 69 | 70 | * Add some code 71 | * Add some screenshots 72 | * Add some explanations 73 |

74 |
-------------------------------------------------------------------------------- /.bit/tests/cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "fixturesFolder": "cypress/fixtures", 3 | "supportFile": false, 4 | "pluginsFile": false 5 | } -------------------------------------------------------------------------------- /.bit/tests/cypress/fixtures/testimage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitprj/Intro-To-Serverless/7fd8024b8d259eb4772785db540bc0e01f34c3c1/.bit/tests/cypress/fixtures/testimage.jpg -------------------------------------------------------------------------------- /.bit/tests/cypress/integration/4.1.spec.js: -------------------------------------------------------------------------------- 1 | describe('Testing Bunnimage', () => { 2 | it('Testing Week 4 Step 1', () => { 3 | cy.visit('bunnimage/index.html') 4 | cy.get('input[type="text"]').type('console.log("hi yall")') 5 | cy.get('input[type="button"]').click() 6 | cy.get('#output').contains('console.log("hi yall")❤️') 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /.bit/tests/cypress/integration/4.2.spec.js: -------------------------------------------------------------------------------- 1 | const uploadFile = (fileName, fileType = '', selector) => { 2 | cy.get(selector).then(subject => { 3 | cy.fixture(fileName, 'base64') 4 | .then(Cypress.Blob.base64StringToBlob) 5 | .then(blob => { 6 | const el = subject[0] 7 | const testFile = new File([blob], fileName, { type: fileType }) 8 | const dataTransfer = new DataTransfer() 9 | dataTransfer.items.add(testFile) 10 | el.files = dataTransfer.files 11 | console.log(el.files) 12 | }) 13 | }) 14 | } 15 | 16 | describe('Test Bunnimage', () => { 17 | it('Testing form submission', () => { 18 | cy.visit('bunnimage/index.html') 19 | const fileName = "testimage.jpg" 20 | cy.get('input[type="text"]').type('acode') 21 | uploadFile(fileName, 'image/jpg', 'input[name="image"]') 22 | cy.get('input[type="submit"]').click() 23 | cy.get('#output').contains('Thanks!') 24 | }) 25 | 26 | it('Testing error catching', () => { 27 | cy.visit('bunnimage/index.html') 28 | const fileName = "testimage.jpg" 29 | uploadFile(fileName, 'image/jpg', 'input[name="image"]') 30 | cy.get('input[type="submit"]').click() 31 | cy.on('window:alert', (str) => { 32 | expect(str).to.equal(`No name error.`) 33 | }) 34 | }) 35 | }) -------------------------------------------------------------------------------- /.bit/tests/cypress/integration/4.3.spec.js: -------------------------------------------------------------------------------- 1 | const uploadFile = (fileName, fileType = '', selector) => { 2 | cy.get(selector).then(subject => { 3 | cy.fixture(fileName, 'base64') 4 | .then(Cypress.Blob.base64StringToBlob) 5 | .then(blob => { 6 | const el = subject[0] 7 | const testFile = new File([blob], fileName, { type: fileType }) 8 | const dataTransfer = new DataTransfer() 9 | dataTransfer.items.add(testFile) 10 | el.files = dataTransfer.files 11 | console.log(el.files) 12 | }) 13 | }) 14 | } 15 | 16 | describe('Test Bunnimage', () => { 17 | it('Testing form submission', () => { 18 | cy.visit('bunnimage/index.html') 19 | const fileName = "testimage.jpg" 20 | cy.get('input[type="text"]').type('mysecret') 21 | uploadFile(fileName, 'image/jpeg', 'input[name="image"]') 22 | cy.get('input[type="submit"]').click() 23 | cy.wait(5000) 24 | cy.get('#output').contains('Your image has been stored successfully!') 25 | }) 26 | }) -------------------------------------------------------------------------------- /.bit/tests/cypress/integration/4.4.spec.js: -------------------------------------------------------------------------------- 1 | const uploadFile = (fileName, fileType = '', selector) => { 2 | cy.get(selector).then(subject => { 3 | cy.fixture(fileName, 'base64') 4 | .then(Cypress.Blob.base64StringToBlob) 5 | .then(blob => { 6 | const el = subject[0] 7 | const testFile = new File([blob], fileName, { type: fileType }) 8 | const dataTransfer = new DataTransfer() 9 | dataTransfer.items.add(testFile) 10 | el.files = dataTransfer.files 11 | console.log(el.files) 12 | }) 13 | }) 14 | } 15 | 16 | describe('Test Bunnimage', () => { 17 | it('Testing upload', () => { 18 | cy.visit('bunnimage/index.html') 19 | const fileName = "testimage.jpg" 20 | cy.get('input[id="username"]').type('acode') 21 | uploadFile(fileName, 'image/jpg', 'input[name="image"]') 22 | cy.get('input[id="button1"]').click() 23 | cy.get('#output').contains('Your image has been stored successfully!') 24 | }) 25 | 26 | it('Testing download', () => { 27 | cy.get('input[id="downloadusername"]').type('acode') 28 | cy.get('input[id="button2"]').click() 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /.bit/tests/cypress/integration/4.5.spec.js: -------------------------------------------------------------------------------- 1 | describe('Testing the CatzAPP', () => { 2 | it('Testing Week 4 Step 5', () => { 3 | cy.visit('catzapp/index.html') 4 | cy.get('input[type="text"]').type('cat') 5 | cy.get('input[type="button"]').click() 6 | cy.get('img').should('have.attr', 'src').should('include','https://cataas.com/cat/says/cat') 7 | }) 8 | }) -------------------------------------------------------------------------------- /.bit/tests/cypress/integration/4.7.spec.js: -------------------------------------------------------------------------------- 1 | describe('Testing the CatzAPP', () => { 2 | it('Testing Week 4 Step 7', () => { 3 | cy.visit('catzapp/index.html') 4 | cy.get('input[id="name1"]').type('cat1') 5 | cy.get('input[id="name2"]').type('cat2') 6 | cy.get('input[id="name3"]').type('cat3') 7 | cy.get('input[id="name4"]').type('cat4') 8 | cy.get('input[type="button"]').click() 9 | cy.wait(25000) 10 | cy.get('img[id="image1"]').should('have.attr', 'src').should('include','data:image/png;base64,') 11 | cy.get('img[id="image2"]').should('have.attr', 'src').should('include','data:image/png;base64,') 12 | cy.get('img[id="image3"]').should('have.attr', 'src').should('include','data:image/png;base64,') 13 | cy.get('img[id="image4"]').should('have.attr', 'src').should('include','data:image/png;base64,') 14 | }) 15 | }) -------------------------------------------------------------------------------- /.bit/tests/cypress/integration/cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "fixturesFolder": "cypress/fixtures", 3 | "supportFile": false, 4 | "pluginsFile": false 5 | } -------------------------------------------------------------------------------- /.bit/tests/cypress/screenshots/4.1.spec.js/Testing Bunnimage -- Testing Week 4 Step 1 (failed).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitprj/Intro-To-Serverless/7fd8024b8d259eb4772785db540bc0e01f34c3c1/.bit/tests/cypress/screenshots/4.1.spec.js/Testing Bunnimage -- Testing Week 4 Step 1 (failed).png -------------------------------------------------------------------------------- /.bit/tests/cypress/videos/4.1.spec.js.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitprj/Intro-To-Serverless/7fd8024b8d259eb4772785db540bc0e01f34c3c1/.bit/tests/cypress/videos/4.1.spec.js.mp4 -------------------------------------------------------------------------------- /.bit/tests/functions.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') //get the methods in the fs package 2 | const fetch = require('node-fetch'); 3 | 4 | const validateResponseStatus = (resp, uri) => { 5 | if (resp.status == 404) { 6 | throw new Error(`Your function could not be found at '${uri}' check function url secret`); 7 | process.exit(1) 8 | } 9 | 10 | if (resp.status == 500) { 11 | throw new Error("Your function has an error and could not be run "); 12 | process.exit(1) 13 | } 14 | } 15 | 16 | const queryString = (uri) => { 17 | if (uri.indexOf("?") === -1) { 18 | uri = uri + "?x=1" 19 | } 20 | return uri 21 | } 22 | 23 | const checkSecret = (secret, secretName) => { 24 | if (!secret || !secret.trim()) { 25 | throw new Error(`You forgot to add your '${secretName}' secret!`); 26 | process.exit(1) 27 | } 28 | } 29 | 30 | const checkCommit = (commit_file) => { 31 | for (var i = 0; i < commit_file.length; i++) { 32 | var a = commit_file[i]; 33 | fs.access(commit_file[i], err => { 34 | if (err) { 35 | throw new Error("You did not commit '" + a + "'") 36 | process.exit(1) 37 | } 38 | }) 39 | } 40 | } 41 | 42 | const throwError = async (error, user, repo) => { 43 | console.log(user) 44 | console.log(repo) 45 | 46 | const endpoint = "https://counselorbot.azurewebsites.net/api/hasuraErrorUpdate?code=qL2oUjo1aUIBdfJe3VhEF41qRQBSnShZwPGr3dujRwvtOGa855fLoA=="; 47 | options = { 48 | method: "POST", 49 | headers: { 50 | user, 51 | repo, 52 | error 53 | } 54 | } 55 | await fetch(endpoint, options) 56 | console.log("fetch has been made") 57 | process.exit(1) 58 | } 59 | 60 | exports.throwError = throwError 61 | 62 | exports.queryString = queryString 63 | exports.validateResponseStatus = validateResponseStatus 64 | exports.checkSecret = checkSecret 65 | exports.checkCommit = checkCommit 66 | exports.throwError = throwError 67 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week1/1-2.helloworld.js: -------------------------------------------------------------------------------- 1 | function hello(){ 2 | return "Hello World"; 3 | } 4 | console.log(hello()); 5 | module.exports = hello; -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week1/1.1-blog.md: -------------------------------------------------------------------------------- 1 | # About Me 2 | My name is Fifi and I enjoy coding! 3 | 4 | ^^Sample response -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week1/1.4-hackervoice.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (context, req) { 2 | 3 | context.log('JavaScript HTTP trigger function processed a request.'); 4 | 5 | let responseValue = "Provide a value in the query parameter 'Password'."; 6 | 7 | if (req.query.password) { 8 | responseValue = req.query.password; 9 | } 10 | 11 | context.res = { 12 | // status: 200, /* Defaults to 200 */ 13 | body: responseValue 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week1/1.5-hackervoice.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (context, req) { 2 | context.log('JavaScript HTTP trigger function processed a request.'); 3 | 4 | let result = ""; 5 | 6 | const password = req.query.password; 7 | 8 | if (password === "letmein") { 9 | result = "Access granted."; 10 | } else { 11 | result = "Access denied."; 12 | } 13 | 14 | context.res = { 15 | // status: 200, /* Defaults to 200 */ 16 | body: result 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week1/1.6-twocatz.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | // npm install node-fetch@2 3 | 4 | module.exports = async function (context, req) { 5 | 6 | context.log('JavaScript HTTP trigger function processed a request.'); 7 | 8 | const cat = await getCatPic(context); 9 | 10 | context.res = { 11 | body: { 12 | cat 13 | } 14 | }; 15 | } 16 | 17 | async function getCatPic(context) { 18 | 19 | const resp = await fetch("https://cataas.com/cat/cute/says/Bitcamp", { 20 | method: 'GET' 21 | }); 22 | 23 | const data = await resp.arrayBuffer(); 24 | 25 | context.log(data); 26 | 27 | const dataInBase64 = Buffer.from(data).toString('base64'); 28 | 29 | return dataInBase64; 30 | } -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week1/1.7-twocatz.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | // npm install node-fetch@2 3 | 4 | module.exports = async function (context, req) { 5 | context.log('JavaScript HTTP trigger function processed a request.'); 6 | 7 | const firstCat = await getCatPic(context); 8 | const secondCat = await getCatPic(context); 9 | 10 | const namesArray = []; 11 | namesArray.push(getNames()); 12 | namesArray.push(getNames()); 13 | 14 | context.res = { 15 | // status: 200, /* Defaults to 200 */ 16 | body: { 17 | cat1: firstCat, 18 | cat2: secondCat, 19 | names: namesArray 20 | } 21 | }; 22 | } 23 | 24 | async function getCatPic(context) { 25 | 26 | const resp = await fetch("https://cataas.com/cat/cute/says/Bitcamp", { 27 | method: 'GET' 28 | }); 29 | 30 | const data = await resp.arrayBuffer(); 31 | 32 | context.log(data); 33 | 34 | const dataInBase64 = Buffer.from(data).toString('base64'); 35 | 36 | return dataInBase64; 37 | } 38 | 39 | function getNames() { 40 | const listOfNames = ["Shreya", "Emily", "Fifi", "Beau", "Evelyn", "Julia", "Daniel", "Fardeen"]; 41 | const randomValue = Math.floor(listOfNames.length * Math.random()); 42 | const resultName = listOfNames[randomValue]; 43 | return resultName; 44 | } 45 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week1/1.8-morse-code.js: -------------------------------------------------------------------------------- 1 | 2 | const morse = require("morse-code-converter"); 3 | // npm install morse-code-converter 4 | 5 | module.exports = async function (context, req) { 6 | context.log('JavaScript HTTP trigger function processed a request.'); 7 | 8 | let code = "Provide a value in the query parameter 'plaintext'"; 9 | 10 | if (req.query.plaintext) { 11 | const english = (req.query.plaintext); 12 | code = morse.textToMorse(english); 13 | } 14 | 15 | context.res = { 16 | // status: 200, /* Defaults to 200 */ 17 | body: code 18 | }; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week2/2.2-emotional.js: -------------------------------------------------------------------------------- 1 | const multipart = require('parse-multipart'); 2 | //npm i parse-multipart 3 | 4 | module.exports = async function (context, req) { 5 | context.log('JavaScript HTTP trigger function processed a request.'); 6 | 7 | const boundary = multipart.getBoundary(req.headers['content-type']); 8 | 9 | const parts = multipart.Parse(req.body, boundary); 10 | 11 | context.log(parts[0]); 12 | 13 | const result = Buffer.from(parts[0].data).toString('base64'); 14 | 15 | context.log(result); 16 | 17 | //TODO analyze the image 18 | 19 | context.res = { 20 | body: result 21 | }; 22 | 23 | }; 24 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week2/2.3-emotional.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const multipart = require('parse-multipart'); 3 | //npm i node-fetch@2 parse-multipart 4 | 5 | module.exports = async function (context, req) { 6 | context.log('JavaScript HTTP trigger function processed a request.'); 7 | 8 | const boundary = multipart.getBoundary(req.headers['content-type']); 9 | 10 | const parts = multipart.Parse(req.body, boundary); 11 | 12 | const result = await analyzeImage(parts[0].data); 13 | 14 | context.res = { 15 | body: { 16 | result 17 | } 18 | // REMEMBER TO RETURN IN JSON! 19 | }; 20 | 21 | console.log(result); 22 | 23 | }; 24 | 25 | async function analyzeImage(byteArray) { 26 | 27 | const subscriptionKey = process.env['subscriptionKey']; 28 | const uriBase = process.env['endpoint'] + '/face/v1.0/detect'; 29 | 30 | let params = new URLSearchParams({ 31 | 'returnFaceId': 'true', 32 | 'returnFaceAttributes': 'emotion' 33 | }); 34 | 35 | const resp = await fetch(uriBase + `?${params.toString()}`, { 36 | method: 'POST', 37 | body: byteArray, 38 | headers: { 39 | 'Content-Type': "application/octet-stream", 40 | "Ocp-Apim-Subscription-Key": subscriptionKey 41 | } 42 | }); 43 | 44 | const data = await resp.json(); 45 | 46 | return data; 47 | }; -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week2/2.4-emotional.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const multipart = require('parse-multipart') 3 | //npm i node-fetch@2 parse-multipart 4 | 5 | module.exports = async function (context, req) { 6 | context.log('JavaScript HTTP trigger function processed a request.'); 7 | 8 | const boundary = multipart.getBoundary(req.headers['content-type']); 9 | 10 | const parts = multipart.Parse(req.body, boundary); 11 | 12 | const result = await analyzeImage(parts[0].data); 13 | 14 | let emotions = result[0].faceAttributes.emotion; 15 | let objects = Object.values(emotions); 16 | 17 | const mainEmotion = Object.keys(emotions).find(key => emotions[key] === Math.max(...objects)); 18 | 19 | context.res = { 20 | body: mainEmotion 21 | }; 22 | 23 | console.log(result); 24 | 25 | }; 26 | 27 | async function analyzeImage(byteArray) { 28 | 29 | const subscriptionKey = process.env['subscriptionKey']; 30 | const uriBase = process.env['endpoint'] + '/face/v1.0/detect'; 31 | 32 | let params = new URLSearchParams({ 33 | 'returnFaceId': 'true', 34 | 'returnFaceAttributes': 'emotion' 35 | }); 36 | 37 | const resp = await fetch(uriBase + `?${params.toString()}`, { 38 | method: 'POST', 39 | body: byteArray, 40 | headers: { 41 | 'Content-Type': "application/octet-stream", 42 | "Ocp-Apim-Subscription-Key": subscriptionKey 43 | } 44 | }); 45 | 46 | const data = await resp.json(); 47 | 48 | return data; 49 | } 50 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week2/2.5-emotional.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const multipart = require('parse-multipart') 3 | // npm i fetch-node@2 parse-multipart 4 | 5 | module.exports = async function (context, req) { 6 | context.log('JavaScript HTTP trigger function processed a request.'); 7 | 8 | const boundary = multipart.getBoundary(req.headers['content-type']); 9 | 10 | const parts = multipart.Parse(req.body, boundary); 11 | 12 | const result = await analyzeImage(parts[0].data); 13 | 14 | let emotions = result[0].faceAttributes.emotion; 15 | let objects = Object.values(emotions); 16 | 17 | const mainEmotion = Object.keys(emotions).find(key => emotions[key] === Math.max(...objects)) 18 | 19 | const apiResult = await fetch("https://api.giphy.com/v1/gifs/translate?s=" + mainEmotion + "&api_key=your_key&limit=1"); 20 | const jsonResult = await apiResult.json(); 21 | 22 | context.res = { 23 | // status: 200, /* Defaults to 200 */ 24 | body: jsonResult.data.url 25 | }; 26 | 27 | console.log(result); 28 | 29 | }; 30 | 31 | async function analyzeImage(byteArray) { 32 | 33 | const subscriptionKey = process.env['subscriptionKey']; 34 | const uriBase = process.env['endpoint'] + '/face/v1.0/detect'; 35 | 36 | let params = new URLSearchParams({ 37 | 'returnFaceId': 'true', 38 | 'returnFaceAttributes': 'emotion' 39 | }); 40 | 41 | const resp = await fetch(uriBase + `?${params.toString()}`, { 42 | method: 'POST', 43 | body: byteArray, 44 | headers: { 45 | 'Content-Type': "application/octet-stream", 46 | "Ocp-Apim-Subscription-Key": subscriptionKey 47 | } 48 | }); 49 | 50 | const data = await resp.json(); 51 | 52 | return data; 53 | } 54 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week2/2.6-songrec.js: -------------------------------------------------------------------------------- 1 | const querystring = require('qs'); 2 | //npm i qs 3 | 4 | module.exports = async function (context, req) { 5 | context.log('JavaScript HTTP trigger function processed a request.'); 6 | 7 | // Parse image link (MediaUrl0) from parameters 8 | const queryObject = querystring.parse(req.body); 9 | 10 | context.log(queryObject); 11 | 12 | const url = queryObject.MediaUrl0; 13 | // the request body you are parsing looks like this: ToCountry=US&MediaContentType0=image%2Fjpeg&ToState=MI&SmsMessageSid=MM0fe83458b74a1f626eb0da4685ab28b5&NumMedia=1&ToCity=UTICA&FromZip=28394&SmsSid=MM0fe83458b74a1f626eb0da4685ab28b5&FromState=NC&SmsStatus=received&FromCity=VASS&Body=&FromCountry=US&To=%2B15869913930&ToZip=48316&NumSegments=1&MessageSid=MM0fe83458b74a1f626eb0da4685ab28b5&AccountSid=ACee62fed677d382600b621e6f24de9bb0&From=%2B19105563874&MediaUrl0=https%3A%2F%2Fapi.twilio.com%2F2010-04-01%2FAccounts%2FACee62fed677d382600b621e6f24de9bb0%2FMessages%2FMM0fe83458b74a1f626eb0da4685ab28b5%2FMedia%2FME29644fd97901859108bc35e210b588f6&ApiVersion=2010-04-01 14 | 15 | context.log(url); 16 | 17 | context.res = { 18 | // status: 200, /* Defaults to 200 */ 19 | body: url 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week2/2.7-songrec.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const querystring = require('qs'); 3 | // npm i node-fetch@2 qs 4 | 5 | module.exports = async function (context, req) { 6 | 7 | context.log('JavaScript HTTP trigger function processed a request.'); 8 | 9 | const queryObject = querystring.parse(req.body); 10 | context.log(queryObject); 11 | 12 | const url = queryObject.MediaUrl0; 13 | 14 | const resp = await fetch(url, { 15 | /* The await expression causes async function execution to pause until a Promise is settled 16 | (that is, fulfilled or rejected), and to resume execution of the async function after fulfillment. 17 | When resumed, the value of the await expression is that of the fulfilled Promise */ 18 | method: 'GET', 19 | }); 20 | 21 | const data = await resp.arrayBuffer(); 22 | const result = await analyzeImage(data); 23 | 24 | context.log(result); 25 | 26 | const age = result[0].faceAttributes.age; 27 | context.log(age); 28 | 29 | let id = "nothing"; 30 | 31 | if (age > 5 && age < 25) { 32 | id = "GenZ"; 33 | } else if (age > 24 && age < 41) { 34 | id = "GenY"; 35 | } else if (age > 40 && age < 57) { 36 | id = "GenX"; 37 | } else if (age > 56 && age < 76) { 38 | id = "BabyBoomers"; 39 | } else { 40 | id = "nothing"; 41 | } 42 | 43 | context.log(id); 44 | 45 | context.res = { 46 | // status: 200, /* Defaults to 200 */ 47 | body: id 48 | }; 49 | }; 50 | 51 | 52 | async function analyzeImage(img) { 53 | 54 | const subscriptionKey = process.env['subscriptionkey']; 55 | const uriBase = process.env['endpoint'] + '/face/v1.0/detect'; 56 | // env variables (similar to .gitignore/.env file) to not expose personal info 57 | // twilio phone-numbers:update "+15869913930" --sms-url="https://twlio-test.azurewebsites.net/api/HttpTrigger1?code=P0wvX4Wrv4iTCT8zw6r356rKLTDzNLC5g5OBfrvoIiJjdUr3jNzr3A==" 58 | 59 | let params = new URLSearchParams({ 60 | 'returnFaceId': 'true', 61 | 'returnFaceAttributes': ['age'] 62 | }); 63 | 64 | let resp = await fetch(uriBase + '?' + params.toString(), { 65 | /* The await expression causes async function execution to pause until a Promise is settled 66 | (that is, fulfilled or rejected), and to resume execution of the async function after fulfillment. 67 | When resumed, the value of the await expression is that of the fulfilled Promise */ 68 | method: 'POST', 69 | body: img, 70 | // we want to send the image 71 | headers: { 72 | 'Content-Type': 'application/octet-stream', 73 | 'Ocp-Apim-Subscription-Key': subscriptionKey 74 | } 75 | }); 76 | 77 | const data = await resp.json(); 78 | 79 | return data; 80 | }; 81 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week2/2.8-songrec.js: -------------------------------------------------------------------------------- 1 | const querystring = require('qs'); 2 | const fetch = require("node-fetch"); 3 | //npm i fetch@2 qs 4 | 5 | module.exports = async function (context, req) { 6 | context.log('JavaScript HTTP trigger function processed a request.'); 7 | 8 | const queryObject = querystring.parse(req.body); 9 | context.log(req.body); 10 | context.log(queryObject); 11 | 12 | const url = queryObject.MediaUrl0; 13 | 14 | let resp = await fetch(url, { 15 | /* The await expression causes async function execution to pause until a Promise is settled 16 | (that is, fulfilled or rejected), and to resume execution of the async function after fulfillment. 17 | When resumed, the value of the await expression is that of the fulfilled Promise */ 18 | method: 'GET', 19 | }); 20 | 21 | // receive the response 22 | const data = await resp.arrayBuffer(); 23 | const result = await analyzeImage(data); 24 | context.log(result); 25 | 26 | const age = result[0].faceAttributes.age; 27 | 28 | let id = "Unknown"; 29 | 30 | context.log(age) 31 | 32 | if (age > 5 && age < 25) { 33 | id = "GenZ"; 34 | } else if (age > 24 && age < 41) { 35 | id = "GenY"; 36 | } else if (age > 40 && age < 57) { 37 | id = "GenX"; 38 | } else if (age > 56 && age < 76) { 39 | id = "BabyBoomers"; 40 | } else { 41 | id = "Unknown"; 42 | } 43 | 44 | context.log(id); 45 | 46 | const songList = { 47 | "GenZ": "https://open.spotify.com/track/0SIAFU49FFHwR3QnT5Jx0k?si=1c12067c9f2b4fbf", 48 | "GenY": "https://open.spotify.com/track/1Je1IMUlBXcx1Fz0WE7oPT?si=a04bbdf6ec4948b9", 49 | "GenX": "https://open.spotify.com/track/4Zau4QvgyxWiWQ5KQrwL43?si=790d9e3ef2ed408d", 50 | "BabyBoomers": "https://open.spotify.com/track/4gphxUgq0JSFv2BCLhNDiE?si=1abb329f2dc24f50", 51 | "Unknown": "https://open.spotify.com/track/5ygDXis42ncn6kYG14lEVG?si=84b49b41d09d4d11" 52 | } 53 | 54 | const song = songList[id]; 55 | 56 | context.res = { 57 | // status: 200, /* Defaults to 200 */ 58 | body: `We guessed you're part of this generation: ${id}! Happy listening! ${song}` 59 | }; 60 | } 61 | 62 | async function analyzeImage(img) { 63 | 64 | const subscriptionKey = process.env['subscriptionkey']; 65 | const uriBase = process.env['endpoint'] + '/face/v1.0/detect'; 66 | // env variables (similar to .gitignore/.env file) to not expose personal info 67 | // twilio phone-numbers:update "+15869913930" --sms-url="https://twlio-test.azurewebsites.net/api/HttpTrigger1?code=P0wvX4Wrv4iTCT8zw6r356rKLTDzNLC5g5OBfrvoIiJjdUr3jNzr3A==" 68 | 69 | let params = new URLSearchParams({ 70 | 'returnFaceId': 'true', 71 | 'returnFaceAttributes': ['age'] 72 | }); 73 | 74 | const resp = await fetch(uriBase + '?' + params.toString(), { 75 | /* The await expression causes async function execution to pause until a Promise is settled 76 | (that is, fulfilled or rejected), and to resume execution of the async function after fulfillment. 77 | When resumed, the value of the await expression is that of the fulfilled Promise */ 78 | method: 'POST', 79 | body: img, 80 | // we want to send the image 81 | headers: { 82 | 'Content-Type': 'application/octet-stream', 83 | 'Ocp-Apim-Subscription-Key': subscriptionKey 84 | } 85 | }); 86 | 87 | // receive the response 88 | const data = await resp.json(); 89 | 90 | return data; 91 | }; -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week3/3.2-bunnimage.js: -------------------------------------------------------------------------------- 1 | const { BlobServiceClient } = require("@azure/storage-blob"); 2 | const parseMultipart = require("parse-multipart"); 3 | 4 | // npm i parse-multipart @azure/storage-blob 5 | 6 | module.exports = async function (context, req) { 7 | 8 | const connectionString = process.env["AZURE_STORAGE_CONNECTION_STRING"]; 9 | 10 | const boundary = parseMultipart.getBoundary(req.headers['content-type']); 11 | const parsedBody = parseMultipart.Parse(req.body, boundary); 12 | 13 | context.log(`The parsed file is: ${parsedBody[0].filename}`); 14 | 15 | const fileType = parsedBody[0].type; 16 | 17 | const ext = getExtByFileType(fileType); 18 | 19 | let responseMessage = ""; 20 | let responseStatus = 200; 21 | 22 | if (ext) { 23 | 24 | responseMessage = await uploadFile(context, connectionString, parsedBody, ext); 25 | 26 | } 27 | else { 28 | // The file type was invalid, we return the corresponding error 29 | responseStatus = 400; 30 | responseMessage = `Invalid file type ${fileType}`; 31 | 32 | } 33 | 34 | context.log(responseMessage) 35 | 36 | context.res = { 37 | status: responseStatus, 38 | body: responseMessage 39 | }; 40 | 41 | 42 | } 43 | 44 | async function uploadFile(context, connectionString, parsedBody, ext) { 45 | 46 | const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString); 47 | // Create a unique name for the container 48 | const containerName = "bunnimage-upload"; 49 | 50 | const blobName = 'test' + "." + ext; 51 | 52 | context.log(`Uploading to Azure storage container ${containerName} as blob: ${blobName}`); 53 | 54 | const containerClient = blobServiceClient.getContainerClient(containerName); 55 | 56 | const blockBlobClient = containerClient.getBlockBlobClient(blobName); 57 | 58 | const uploadBlobResponse = await blockBlobClient.upload(parsedBody[0].data, parsedBody[0].data.length); 59 | 60 | context.log(`Blob was uploaded successfully. requestId: ${uploadBlobResponse.requestId}`); 61 | 62 | return `File ${parsedBody[0].filename} saved successfully as ${blobName}`; 63 | } 64 | 65 | function getExtByFileType(fileType) { 66 | 67 | let ext = ""; 68 | 69 | if (fileType === "image/png") { 70 | ext = "png"; 71 | } else if (fileType === "image/jpeg") { 72 | ext = "jpeg"; 73 | } 74 | 75 | return ext; 76 | 77 | } -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week3/3.3-bunnimage.js: -------------------------------------------------------------------------------- 1 | const fetch = require("node-fetch"); 2 | const { BlobServiceClient } = require("@azure/storage-blob"); 3 | const parseMultipart = require("parse-multipart"); 4 | 5 | // npm i parse-multipart node-fetch@2 @azure/storage-blob 6 | 7 | module.exports = async function (context, req) { 8 | 9 | const connectionString = process.env["AZURE_STORAGE_CONNECTION_STRING"]; 10 | 11 | /* 12 | Get the filename via the route defined in function.json 13 | (see https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook-trigger?tabs=in-process%2Cfunctionsv2&pivots=programming-language-javascript#customize-the-http-endpoint) 14 | 15 | The entry looks like this: 16 | "bindings": [ 17 | { 18 | "authLevel": "function", 19 | "type": "httpTrigger", 20 | "direction": "in", 21 | "route": "bunnimage-upload/{filename:alpha}", 22 | "name": "req", 23 | "methods": [ 24 | "post" 25 | ] 26 | }, 27 | */ 28 | const fileName = context.bindingData.filename; 29 | const boundary = parseMultipart.getBoundary(req.headers['content-type']); 30 | const parsedBody = parseMultipart.Parse(req.body, boundary); 31 | 32 | context.log(`The parsed file is: ${parsedBody[0].filename}`); 33 | 34 | const fileType = parsedBody[0].type; 35 | 36 | const ext = getExtByFileType(fileType); 37 | 38 | let responseMessage = ""; 39 | let responseStatus = 200; 40 | 41 | if (ext) { 42 | 43 | responseMessage = await uploadFile(context, connectionString, parsedBody, ext, fileName); 44 | 45 | } 46 | else { 47 | // The file type was invalid, we return the corresponding error 48 | responseStatus = 400; 49 | responseMessage = `Invalid file type ${fileType}`; 50 | 51 | } 52 | 53 | context.log(responseMessage) 54 | 55 | context.res = { 56 | status: responseStatus, 57 | body: responseMessage 58 | }; 59 | 60 | 61 | } 62 | 63 | async function uploadFile(context, connectionString, parsedBody, ext, fileName) { 64 | 65 | const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString); 66 | // Create a unique name for the container 67 | const containerName = "bunnimage-upload"; 68 | 69 | const blobName = fileName + "." + ext; 70 | 71 | context.log(`Uploading to Azure storage container ${containerName} as blob: ${blobName}`); 72 | 73 | const containerClient = blobServiceClient.getContainerClient(containerName); 74 | 75 | const blockBlobClient = containerClient.getBlockBlobClient(blobName); 76 | 77 | const uploadBlobResponse = await blockBlobClient.upload(parsedBody[0].data, parsedBody[0].data.length); 78 | 79 | context.log(`Blob was uploaded successfully. requestId: ${uploadBlobResponse.requestId}`); 80 | 81 | return `File ${parsedBody[0].filename} saved successfully as ${blobName}`; 82 | } 83 | 84 | function getExtByFileType(fileType) { 85 | 86 | let ext = ""; 87 | 88 | if (fileType === "image/png") { 89 | ext = "png"; 90 | } else if (fileType === "image/jpeg") { 91 | ext = "jpeg"; 92 | } 93 | 94 | return ext; 95 | 96 | } -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week3/3.4-bunnimage.js: -------------------------------------------------------------------------------- 1 | const { BlobServiceClient } = require("@azure/storage-blob"); 2 | 3 | /* 4 | Timer-triggered Azure Function need a storage account 5 | When developing locally do not forget to set the "AzureWebJobsStorage": "UseDevelopmentStorage=true" 6 | in the local.settings.json file 7 | */ 8 | 9 | //Cron values (see function.json - schedule) 10 | //0 */5 * * * * 11 | //https://crontab.guru/every-5-minutes 12 | 13 | module.exports = async function (context, myTimer) { 14 | 15 | const connectionString = process.env["AZURE_STORAGE_CONNECTION_STRING"]; 16 | const deleteContainerName = "images"; 17 | 18 | const blobContainerClient = await BlobServiceClient.fromConnectionString(connectionString).getContainerClient(deleteContainerName); 19 | for await (const blob of blobContainerClient.listBlobsFlat()) { 20 | context.log(`Deleting blob name ${blob.name}`); 21 | // log in console what file you are deleting 22 | await blobContainerClient.deleteBlob(blob.name); 23 | } 24 | context.log(`All blobs of container ${deleteContainerName} deleted`); 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week3/3.5-bunnimage.js: -------------------------------------------------------------------------------- 1 | const fetch = require("node-fetch"); 2 | // npm i node-fetch@2 3 | 4 | module.exports = async function (context, req, inputBlob) { 5 | 6 | let downloadUri = "" 7 | let success = false 8 | 9 | const fileName = context.bindingData.filename; 10 | 11 | // replace with your own blob storage URL and make sure to make the container public! 12 | const downloadPng = "https://bunnimagestorage.blob.core.windows.net/images/" + fileName + ".png"; 13 | const downloadJpg = "https://bunnimagestorage.blob.core.windows.net/images/" + fileName + ".jpeg"; 14 | 15 | const pngResp = await fetch(downloadPng, { 16 | method: 'GET', 17 | }) 18 | 19 | const pngData = await pngResp; 20 | 21 | const jpgResp = await fetch(downloadJpg, { 22 | method: 'GET', 23 | }) 24 | 25 | const jpgData = await jpgResp; 26 | 27 | if (pngData.status !== 200 && jpgData.status !== 200) { 28 | success = false; 29 | } else if (pngData.status === 200) { 30 | success = true; 31 | downloadUri = downloadPng 32 | } else if (jpgData.status === 200) { 33 | success = true; 34 | downloadUri = downloadJpg 35 | } 36 | 37 | context.log(`Success status of request for file ${fileName}: ${success}`); 38 | 39 | context.res = { 40 | body: { 41 | "downloadUri": downloadUri, 42 | "success": success, 43 | } 44 | }; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week3/3.6-deepsecrets.js: -------------------------------------------------------------------------------- 1 | const qs = require('qs'); 2 | const MessagingResponse = require('twilio').twiml.MessagingResponse; 3 | //npm i qs twilio 4 | 5 | module.exports = async function (context, req) { 6 | 7 | const queryObject = qs.parse(req.body); 8 | const message = queryObject.Body; 9 | 10 | const twiml = new MessagingResponse(); 11 | twiml.message('You said: ' + message); 12 | 13 | context.res = { 14 | status: 200, 15 | body: twiml.toString(), 16 | headers: { 'Content-Type': 'application/xml' }, 17 | isRaw: true 18 | }; 19 | 20 | } 21 | 22 | // https://www.twilio.com/docs/sms/quickstart/node 23 | // https://www.twilio.com/docs/usage/tutorials/serverless-webhooks-azure-functions-and-node-js 24 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week3/3.7-deepsecrets.js: -------------------------------------------------------------------------------- 1 | const qs = require('qs'); 2 | const CosmosClient = require("@azure/cosmos").CosmosClient; 3 | // npm i qs @azure/cosmos 4 | 5 | module.exports = async function (context, req) { 6 | 7 | const queryObject = qs.parse(req.body); 8 | const message = queryObject.Body; 9 | 10 | const document = { "message": message }; 11 | 12 | const items = await createDocument(document); 13 | 14 | let responseMessage = ""; 15 | 16 | if (items.length > 0) { 17 | 18 | context.log(`The fetched item is: ${JSON.stringify(items[0].message)}`); 19 | responseMessage = `Thanks 😊! Stored your secret "${message}". 😯 Someone confessed that: ${JSON.stringify(items[0].message)}`; 20 | 21 | } else { 22 | 23 | context.log(`First item entered in DB`); 24 | responseMessage = `Thanks 😊! Stored your secret "${message}".`; 25 | 26 | } 27 | context.res = { 28 | body: responseMessage 29 | }; 30 | 31 | } 32 | 33 | function getCosmosDBConfig() { 34 | const config = { 35 | endpoint: process.env.COSMOSDB_ENDPOINT, 36 | key: process.env.COSMOSDB_KEY, 37 | databaseId: "SecretStorer", 38 | containerId: "secrets", 39 | partitionKey: { kind: "Hash", paths: ["/secrets"] } 40 | }; 41 | 42 | return config 43 | } 44 | 45 | async function createDbAndContainer(client, databaseId, containerId, partitionKey) { 46 | await client.databases.createIfNotExists({ id: databaseId }); 47 | 48 | await client.database(databaseId) 49 | .containers.createIfNotExists( 50 | { id: containerId, key: partitionKey }, 51 | { offerThroughput: 400 } 52 | ); 53 | } 54 | 55 | async function createDocument(newItem) { 56 | 57 | const cosmosDBConfig = getCosmosDBConfig(); 58 | const cosmosDbEndpoint = cosmosDBConfig.endpoint; 59 | const cosmosDbKey = cosmosDBConfig.key; 60 | 61 | const client = new CosmosClient({ endpoint: cosmosDbEndpoint, key: cosmosDbKey }); 62 | const database = client.database(cosmosDBConfig.databaseId); 63 | const container = database.container(cosmosDBConfig.containerId); 64 | await createDbAndContainer(client, cosmosDBConfig.databaseId, cosmosDBConfig.containerId, cosmosDBConfig.partitionKey); 65 | 66 | const querySpec = { 67 | query: "SELECT top 1 * FROM c order by c._ts desc" 68 | }; 69 | 70 | // read all items in the Items container before creating a new one 71 | const { resources: items } = await container.items 72 | .query(querySpec) 73 | .fetchAll(); 74 | 75 | // Create the new entry and return the old one 76 | await container.items.create(newItem); 77 | return items 78 | } -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week3/3.8-deepsecrets.js: -------------------------------------------------------------------------------- 1 | const qs = require('qs'); 2 | const CosmosClient = require("@azure/cosmos").CosmosClient; 3 | // npm i qs @azure/cosmos 4 | 5 | module.exports = async function (context, req) { 6 | 7 | const queryObject = qs.parse(req.body); 8 | const message = queryObject.Body; 9 | 10 | const document = { "message": message }; 11 | 12 | const items = await createDocument(document); 13 | 14 | let responseMessage = ""; 15 | 16 | if (items.length > 0) { 17 | 18 | const randomItem = getRandomItem(items); 19 | 20 | context.log(`The fetched item is: ${JSON.stringify(randomItem.message)}`); 21 | responseMessage = `Thanks 😊! Stored your secret "${message}". 😯 Someone confessed that: ${JSON.stringify(randomItem.message)}`; 22 | 23 | } else { 24 | 25 | context.log(`First item entered in DB`); 26 | responseMessage = `Thanks 😊! Stored your secret "${message}".`; 27 | 28 | } 29 | context.res = { 30 | body: responseMessage 31 | }; 32 | 33 | } 34 | 35 | function getCosmosDBConfig() { 36 | const config = { 37 | endpoint: process.env["COSMOSDB_ENDPOINT"], 38 | key: process.env["COSMOSDB_KEY"], 39 | databaseId: "SecretStorer", 40 | containerId: "secrets", 41 | partitionKey: { kind: "Hash", paths: ["/secrets"] } 42 | }; 43 | 44 | return config 45 | } 46 | 47 | function getRandomItem(items) { 48 | const random_value = Math.floor(items.length * Math.random()) 49 | return items[random_value] 50 | } 51 | 52 | async function createDbAndContainer(client, databaseId, containerId, partitionKey) { 53 | await client.databases.createIfNotExists({ id: databaseId }); 54 | 55 | await client.database(databaseId) 56 | .containers.createIfNotExists( 57 | { id: containerId, key: partitionKey }, 58 | { offerThroughput: 400 } 59 | ); 60 | } 61 | 62 | async function createDocument(newItem) { 63 | 64 | const cosmosDBConfig = getCosmosDBConfig(); 65 | const cosmosDbEndpoint = cosmosDBConfig.endpoint; 66 | const cosmosDbKey = cosmosDBConfig.key; 67 | 68 | const client = new CosmosClient({ endpoint: cosmosDbEndpoint, key: cosmosDbKey }); 69 | const database = client.database(cosmosDBConfig.databaseId); 70 | const container = database.container(cosmosDBConfig.containerId); 71 | await createDbAndContainer(client, cosmosDBConfig.databaseId, cosmosDBConfig.containerId, cosmosDBConfig.partitionKey); 72 | 73 | const querySpec = { 74 | query: "SELECT * from c" 75 | }; 76 | 77 | // read all items in the Items container before creating a new one 78 | const { resources: items } = await container.items 79 | .query(querySpec) 80 | .fetchAll(); 81 | 82 | // Create the new entry and return the old one 83 | await container.items.create(newItem); 84 | return items 85 | } 86 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week3/deepsecrets.js: -------------------------------------------------------------------------------- 1 | const querystring = require('qs'); 2 | const CosmosClient = require("@azure/cosmos").CosmosClient; 3 | // npm install @azure/cosmos 4 | 5 | const config = { 6 | endpoint: process.env["ENDPOINT"], 7 | key: process.env["KEY"], 8 | databaseId: "SecretStorer", 9 | containerId: "secrets", 10 | partitionKey: {kind: "Hash", paths: ["/secrets"]} 11 | }; 12 | 13 | async function create(client, databaseId, containerId) { 14 | const { database } = await client.databases.createIfNotExists({ 15 | id: config.databaseId 16 | }); 17 | 18 | const { container } = await client 19 | .database(config.databaseId) 20 | .containers.createIfNotExists( 21 | { id: config.containerId, key: config.partitionKey }, 22 | { offerThroughput: 400 } 23 | ); 24 | } 25 | 26 | async function createDocument(newItem) { 27 | var { endpoint, key, databaseId, containerId } = config; 28 | const client = new CosmosClient({endpoint, key}); 29 | const database = client.database(databaseId); 30 | const container = database.container(containerId); 31 | await create(client, databaseId, containerId); 32 | 33 | const querySpec = { 34 | query: "SELECT * from c" 35 | }; 36 | 37 | // read all items in the Items container 38 | const { resources: items } = await container.items 39 | .query(querySpec) 40 | .fetchAll(); 41 | 42 | const {resource: createdItem} = await container.items.create(newItem); 43 | return items 44 | } 45 | 46 | module.exports = async function (context, req) { 47 | context.log('JavaScript HTTP trigger function processed a request.'); 48 | 49 | const queryObject = querystring.parse(req.body); 50 | message = queryObject.Body; 51 | let document = {"message":message} 52 | 53 | let items = await createDocument(document) 54 | context.log(items) 55 | var random_value = Math.floor(items.length * Math.random()) 56 | 57 | const responseMessage = `Thanks 😊! Stored your secret "${message}". 😯 Someone confessed that: ${JSON.stringify(items[random_value].message)}` 58 | 59 | context.res = { 60 | // status: 200, /* Defaults to 200 */ 61 | body: responseMessage 62 | }; 63 | } 64 | 65 | // https://www.twilio.com/docs/sms/quickstart/node 66 | // https://www.neilwithdata.com/azure-functions-post-body-js 67 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week4/4.1-bunnimage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Bitproject - Event Listener 8 | 9 | 10 | 11 |
12 |
13 |

Give your code some love ❤️

14 | 15 |
16 |
17 | 18 | 19 |
20 |
21 |
22 |
23 |

Bitprj © 2022

24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week4/4.1-bunnimage/script.js: -------------------------------------------------------------------------------- 1 | const bunnForm = document.getElementById("bunnForm"); 2 | bunnForm.addEventListener("submit", function (event) { 3 | event.preventDefault(); 4 | const username = document.getElementById("username").value 5 | const output = document.getElementById("output") 6 | output.textContent = username + "❤" 7 | }); -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week4/4.2-bunnimage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Bitproject - Error Handling 8 | 9 | 10 | 11 |
12 |
13 |

Bunnimage 🐇

14 |
15 | 16 |

17 | 18 |

19 | 20 |
21 |
22 |
23 |
24 |
25 |

Bitprj © 2022

26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week4/4.2-bunnimage/script.js: -------------------------------------------------------------------------------- 1 | const bunnForm = document.getElementById('bunnForm'); 2 | 3 | bunnForm.addEventListener('submit', function (event) { 4 | event.preventDefault(); 5 | 6 | // Check the the File is a JPEG or PNG 7 | const image = document.getElementById('image').files[0]; 8 | 9 | if (image.type !== 'image/jpeg' && image.type !== 'image/png') { 10 | alert('Please upload a JPEG or PNG file'); 11 | return; 12 | } 13 | 14 | // Check that the file name is supplied 15 | if (document.getElementById('filename').value === '') { 16 | alert('Please provide a file name.') 17 | return; 18 | } 19 | 20 | const output = document.getElementById("output") 21 | output.textContent = 'Thanks!' 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week4/4.3-bunnimage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Making fetch Happen 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

Bunnimage 🐇

17 |
18 | 19 |

20 | 21 |

22 | 23 |
24 |
25 |
26 |
27 |
28 |

Bitprj © 2022

29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week4/4.3-bunnimage/script.js: -------------------------------------------------------------------------------- 1 | const bunnForm = document.getElementById('bunnForm'); 2 | 3 | bunnForm.addEventListener('submit', function (event) { 4 | event.preventDefault(); 5 | 6 | // Check the the File is a JPEG or PNG 7 | const image = document.getElementById('image').files[0]; 8 | 9 | if (image.type !== 'image/jpeg' && image.type !== 'image/png') { 10 | alert('Please upload a JPEG or PNG file'); 11 | return; 12 | } 13 | 14 | // Check that the file name is supplied 15 | if (document.getElementById('filename').value === '') { 16 | alert('Please provide a file name.') 17 | return; 18 | } 19 | 20 | const myForm = document.getElementById("bunnForm") 21 | const payload = new FormData(myForm); 22 | 23 | console.log(payload); 24 | 25 | const userName = document.getElementById("username").value; 26 | const output = document.getElementById("output"); 27 | 28 | console.log("Posting your image..."); 29 | 30 | const resp = await fetch("YOUR_URL", { 31 | method: 'POST', 32 | headers: { 33 | 'codename': userName 34 | }, 35 | body: payload 36 | }); 37 | 38 | const data = await resp.text(); 39 | console.log(data); 40 | output.textContent = "Your image has been stored successfully!"; 41 | 42 | }); -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week4/4.4-bunnimage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Downloading your image! 8 | 9 | 10 | 11 |
12 |
13 |

Bunnimage 🐇

14 |

Upload

15 |
16 | 17 |

18 | 19 |

20 | 21 |
22 |
23 |
24 |

Download

25 | 26 |

27 | 28 |
29 |
30 |

Bitprj © 2022

31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week4/4.4-bunnimage/script.js: -------------------------------------------------------------------------------- 1 | const bunnForm = document.getElementById('bunnForm'); 2 | 3 | bunnForm.addEventListener('submit', function (event) { 4 | event.preventDefault() 5 | 6 | // Check the the File is a JPEG or PNG 7 | const image = document.getElementById('image').files[0]; 8 | 9 | if (image.type !== 'image/jpeg' && image.type !== 'image/png') { 10 | alert('Please upload a JPEG or PNG file'); 11 | return; 12 | } 13 | 14 | // Check that the file name is supplied 15 | if (document.getElementById('filename').value === '') { 16 | alert('Please provide a file name.') 17 | return; 18 | } 19 | 20 | const myForm = document.getElementById("bunnForm") 21 | const payload = new FormData(myForm); 22 | 23 | console.log(payload); 24 | 25 | const userName = document.getElementById("username").value; 26 | const output = document.getElementById("output"); 27 | 28 | console.log("Posting your image..."); 29 | 30 | const resp = await fetch("YOUR_URL", { 31 | method: 'POST', 32 | headers: { 33 | 'codename': userName 34 | }, 35 | body: payload 36 | }); 37 | 38 | const data = await resp.text(); 39 | console.log(data); 40 | output.textContent = "Your image has been stored successfully!"; 41 | 42 | }); 43 | 44 | const downloadButton = document.getElementById("button2"); 45 | 46 | downloadButton.addEventListener("click", () => downloadImage()); 47 | 48 | async function downloadImage() { 49 | 50 | const username = document.getElementById("downloadusername").value; 51 | 52 | console.log("Attempting to get your pdf..."); 53 | 54 | const resp = await fetch("INSERT_DOWNLOAD)URL", { 55 | method: 'GET', 56 | headers: { 57 | 'username': username 58 | }, 59 | }); 60 | 61 | const data = await resp.json(); 62 | 63 | console.log("PDF link received!") 64 | console.log(data.downloadUri) 65 | console.log(data.success) 66 | 67 | const link = data.downloadUri 68 | 69 | window.open(link, "_self") 70 | } 71 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week4/4.5-twocatz/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitprj/Intro-To-Serverless/7fd8024b8d259eb4772785db540bc0e01f34c3c1/.bit/tests/sample-solutions/week4/4.5-twocatz/cat.jpg -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week4/4.5-twocatz/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Create the CataaS Website 8 | 9 | 10 | 11 |
12 |
13 |

Pet API 🐱

14 |
15 | 16 |
17 | 18 |
19 | 20 |
21 |
22 |
23 | 24 |
25 |
26 |

Bitprj © 2022

27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week4/4.5-twocatz/script.js: -------------------------------------------------------------------------------- 1 | const submitButton = document.getElementById("submit"); 2 | 3 | submitButton.addEventListener("click", function (event) { 4 | event.preventDefault(); 5 | 6 | if (document.getElementById("name").value !== '') { 7 | const cat = document.getElementById("name").value; 8 | document.getElementById("image").src = "https://bit-cat.azurewebsites.net/cat/says/" + cat 9 | }; 10 | }); -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week4/4.6-twocatz/twocatz.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch') 2 | // npm i node-fetch@2 3 | 4 | module.exports = async function (context, req) { 5 | 6 | const name1 = req.query.name1; 7 | const name2 = req.query.name2; 8 | const name3 = req.query.name3; 9 | const name4 = req.query.name4; 10 | 11 | const firstCat = await getCatPic(name1, context); 12 | const secondCat = await getCatPic(name2, context); 13 | const thirdCat = await getCatPic(name3, context); 14 | const fourthCat = await getCatPic(name4, context); 15 | 16 | context.res = { 17 | body: { 18 | cat1: firstCat, 19 | cat2: secondCat, 20 | cat3: thirdCat, 21 | cat4: fourthCat 22 | } 23 | }; 24 | } 25 | 26 | async function getCatPic(name, context) { 27 | 28 | const resp = await fetch("https://bit-cat.azurewebsites.net/cat/says/" + name, { 29 | method: 'GET' 30 | }); 31 | 32 | const data = await resp.arrayBuffer() 33 | context.log(data) 34 | const convertedData = Buffer.from(data).toString('base64') 35 | return convertedData 36 | } 37 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week4/4.7-twocatz/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitprj/Intro-To-Serverless/7fd8024b8d259eb4772785db540bc0e01f34c3c1/.bit/tests/sample-solutions/week4/4.7-twocatz/cat.jpg -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week4/4.7-twocatz/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Ultimate CAT INVASION! 8 | 9 | 10 | 11 |
12 |
13 |

Pet API 🐱

14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | 24 |
25 |
26 |
27 | Red dot 28 | Red dot 29 | Red dot 30 | Red dot 31 |
32 |
33 |

Bitprj © 2022

34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /.bit/tests/sample-solutions/week4/4.7-twocatz/script.js: -------------------------------------------------------------------------------- 1 | const submitButton = document.getElementById("submit"); 2 | 3 | submitButton.addEventListener("click", function (event) { 4 | event.preventDefault(); 5 | 6 | const AZURE_URL = "Put your Azure Function URL here"; 7 | 8 | const url = `${AZURE_URL}&name1=${document.getElementById("name1").value}&name2=${document.getElementById("name2").value}&name3=${document.getElementById("name3").value}&name4=${document.getElementById("name4").value}` 9 | 10 | const resp = await fetch(url, { 11 | method: 'GET' 12 | }); 13 | 14 | const result = await resp.json() 15 | 16 | document.getElementById("image1").src = "data:image/png;base64," + result.cat1 17 | document.getElementById("image2").src = "data:image/png;base64," + result.cat2 18 | document.getElementById("image3").src = "data:image/png;base64," + result.cat3 19 | document.getElementById("image4").src = "data:image/png;base64," + result.cat4 20 | }); -------------------------------------------------------------------------------- /.bit/tests/spec.js: -------------------------------------------------------------------------------- 1 | const cypress = require('cypress') 2 | const functions = require('./functions'); 3 | const fetch = require('node-fetch'); 4 | const args = require('minimist')(process.argv.slice(2)) 5 | const user = args['user']; 6 | const repo = args['repo']; 7 | const spec = args['spec']; 8 | 9 | console.log(spec) 10 | 11 | 12 | cypress 13 | .run({ 14 | // the path is relative to the current working directory 15 | spec: spec, 16 | }) 17 | .then(async (results) => { 18 | // console.log(results) 19 | if (results.totalFailed > 0) { 20 | let msg = `Total Failed: ${results.totalFailed} Total Passed: ${results.totalPassed}` 21 | await functions.throwError(msg, user, repo); 22 | } 23 | 24 | }) -------------------------------------------------------------------------------- /.bit/tests/test.1.2.js: -------------------------------------------------------------------------------- 1 | const args = require('minimist')(process.argv.slice(2)) 2 | const functions = require('./functions'); 3 | const user = args['user']; 4 | const repo = args['repo']; 5 | 6 | async function main() { 7 | try { hello = require('./../../week1/helloworld.js') } 8 | catch (e) { 9 | await functions.throwError("Searching for 'helloworld.js'... file cannot be found", user, repo) 10 | console.log("Searching for 'helloworld.js'... file cannot be found"); 11 | process.exit(1) 12 | } 13 | 14 | let helloworld = hello() 15 | let test_output = "Hello World" 16 | 17 | if (helloworld != test_output) { 18 | await functions.throwError(`Got: '${helloworld}', was expecting: '${test_output}'.`, user, repo) 19 | console.log(`Got: "${helloworld}", was expecting: "${test_output}".`) 20 | process.exit(1) 21 | } 22 | 23 | console.info("Yay! 🎉🎉🎉🎉🎉🎉🎉🍾") 24 | } 25 | 26 | main(); 27 | -------------------------------------------------------------------------------- /.bit/tests/test.1.4.js: -------------------------------------------------------------------------------- 1 | 2 | let uri; 3 | const fetch = require('node-fetch'); 4 | const args = require('minimist')(process.argv.slice(2)) 5 | const functions = require('./functions'); 6 | const user = args['user']; 7 | const repo = args['repo']; 8 | 9 | async function main() { 10 | uri = process.env.HACKERVOICE_ENDPOINT; 11 | 12 | try { 13 | functions.checkSecret(uri, "HACKERVOICE_ENDPOINT") 14 | const commit_file = ['hackervoice/index.js'] 15 | functions.checkCommit(commit_file) 16 | 17 | uri = functions.queryString(uri) 18 | 19 | test1 = "fifiiscool"; 20 | uri1 = uri + "&password=" + test1; 21 | 22 | 23 | const resp = await fetch(uri1, { 24 | method: 'GET' 25 | }); 26 | var data = await resp.text() 27 | 28 | functions.validateResponseStatus(resp, uri) 29 | 30 | if (data == test1) { 31 | console.log("Yay! 🎉 We got: " + JSON.stringify(data) + ", which matches our input.") 32 | } else { 33 | console.error("We got this " + JSON.stringify(data) + ". We should have gotten our input, 'fifiiscool' ... Try again!") 34 | await functions.throwError("We got this " + JSON.stringify(data) + ". We should have gotten our input, 'fifiiscool' ... Try again!", user, repo) 35 | process.exit(1) 36 | } 37 | } 38 | catch (e) { 39 | console.error(e); 40 | await functions.throwError(e, user, repo) 41 | } 42 | } 43 | 44 | main(); 45 | -------------------------------------------------------------------------------- /.bit/tests/test.1.5.js: -------------------------------------------------------------------------------- 1 | 2 | let uri; 3 | const fetch = require('node-fetch'); 4 | const args = require('minimist')(process.argv.slice(2)) 5 | const functions = require('./functions'); 6 | const user = args['user']; 7 | const repo = args['repo']; 8 | 9 | async function main() { 10 | try { 11 | uri = process.env.HACKERVOICE_ENDPOINT 12 | 13 | functions.checkSecret(uri, "HACKERVOICE_ENDPOINT") 14 | 15 | //If we have no query string then add one 16 | //this allows us to append without error 17 | uri = functions.queryString(uri) 18 | 19 | const uriWithQuery = uri + "&password=letmein" 20 | 21 | const resp = await fetch(uriWithQuery, { 22 | method: 'GET' 23 | }); 24 | 25 | functions.validateResponseStatus(resp, uri) 26 | 27 | var correct = await resp.text() 28 | 29 | const response = await fetch(uri + "&password=incorrect", { 30 | method: 'GET' 31 | }); 32 | 33 | var incorrect = await response.text() 34 | 35 | try { 36 | if (correct == "Access granted." && incorrect == "Access denied.") { 37 | console.info("Yay! 🎉 You didn't let the bad guys in.") 38 | } else { 39 | console.error("Try again!") 40 | console.error(`We submitted "letmein" and got "${correct}", which should equal "Access granted."`) 41 | console.error(`We submitted "incorrect" and got "${incorrect}", which should equal "Access denied."`) 42 | await functions.throwError("We got the wrong response, you let the bad guys in!", user, repo) 43 | process.exit(1) 44 | } 45 | } catch (e) { 46 | console.error("Are you sure you returned something to us? We didn't get anything. Try again!") 47 | await functions.throwError("Are you sure you returned something to us? We didn't get anything. Try again!", user, repo) 48 | process.exit(1) 49 | } 50 | 51 | 52 | } 53 | catch (e) { 54 | await functions.throwError(e, user, repo) 55 | } 56 | } 57 | 58 | main(); 59 | -------------------------------------------------------------------------------- /.bit/tests/test.1.6.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const args = require('minimist')(process.argv.slice(2)) 3 | const functions = require('./functions'); 4 | const user = args['user']; 5 | const repo = args['repo']; 6 | 7 | let uri = process.env.TWOCATZ_ENDPOINT 8 | 9 | async function main() { 10 | try { 11 | functions.checkSecret(uri, "TWOCATZ_ENDPOINT") 12 | 13 | //if you wanna add more files, just put a comma after the filename (array) 14 | const commit_file = ['twocatz/index.js'] 15 | 16 | functions.checkCommit(commit_file) 17 | 18 | const resp = await fetch(uri, { 19 | method: 'GET' 20 | }); 21 | var data = await resp.json() 22 | let test = JSON.stringify(data) 23 | 24 | functions.validateResponseStatus(resp, uri) 25 | 26 | if (test.length < 3) { 27 | console.error("No response... Try again!") 28 | process.exit(1) 29 | } 30 | try { 31 | var catimage = test; 32 | var newCat = Buffer.from(catimage, 'base64').toString('ascii') 33 | console.info("Yay! 🎉 We got your cat picture 🐱") 34 | } catch (e) { 35 | console.error("Sorry! We couldn't find one or both of the cat pictures. Make sure you encoded in BASE64!") 36 | await functions.throwError("Sorry! We couldn't find one or both of the cat pictures. Make sure you encoded in BASE64!", user, repo) 37 | process.exit(1) 38 | } 39 | 40 | } 41 | catch (e) { 42 | await functions.throwError(e, user, repo) 43 | } 44 | } 45 | 46 | main(); 47 | -------------------------------------------------------------------------------- /.bit/tests/test.1.7.js: -------------------------------------------------------------------------------- 1 | let uri; 2 | const fetch = require('node-fetch'); 3 | const args = require('minimist')(process.argv.slice(2)) 4 | const functions = require('./functions'); 5 | const user = args['user']; 6 | const repo = args['repo']; 7 | 8 | async function main() { 9 | try { 10 | 11 | uri = process.env.TWOCATZ_ENDPOINT 12 | functions.checkSecret(uri, "TWOCATZ_ENDPOINT") 13 | 14 | try { 15 | const resp = await fetch(uri, { 16 | method: 'GET' 17 | }); 18 | var data = await resp.json() 19 | let test = JSON.stringify(data) 20 | 21 | functions.validateResponseStatus(resp, uri) 22 | 23 | if (test.length < 3) { 24 | console.error("No response... Try again!") 25 | await functions.throwError("No response... Try again!", user, repo) 26 | process.exit(1) 27 | } 28 | } catch (e) { 29 | console.error("Did you return valid JSON? Try again!") 30 | await functions.throwError("Did you return valid JSON? Try again!", user, repo) 31 | process.exit(1) 32 | } 33 | 34 | 35 | try { 36 | var catimage1 = data.cat1; 37 | var catimage2 = data.cat2; 38 | var newCat1 = Buffer.from(catimage1, 'base64').toString('ascii') 39 | var newCat2 = Buffer.from(catimage2, 'base64').toString('ascii') 40 | console.info("Yay! 🎉 We got your cat pictures 🐱") 41 | } catch (e) { 42 | console.error("Sorry! We couldn't find one or both of the cat pictures. Make sure you encoded in BASE64!") 43 | await functions.throwError("Sorry! We couldn't find one or both of the cat pictures. Make sure you encoded in BASE64!", user, repo) 44 | process.exit(1) 45 | } 46 | 47 | var array = ["Shreya", "Emily", "Fifi", "Beau", "Evelyn", "Julia", "Daniel", "Fardeen"] 48 | var names = data.names 49 | var name1 = names[0] 50 | var name2 = names[1] 51 | 52 | try { 53 | if (array.includes(name1) && array.includes(name2)) { 54 | console.info(`Yay! 🎉 Thanks for getting our names right. We got: "${name1}" and "${name2}"`) 55 | } else { 56 | console.error(`Sorry, your names, "${name1}" and "${name2}", were not correct.`) 57 | await functions.throwError(`Sorry, your names, '${name1}' and '${name2}', were not correct.`, user, repo) 58 | process.exit(1) 59 | } 60 | } catch (e) { 61 | console.error(`Sorry, your names, '${name1}' and '${name2}', were not correct.`) 62 | await functions.throwError(e, user, repo) 63 | process.exit(1) 64 | } 65 | } 66 | catch (e) { 67 | await functions.throwError(e, user, repo) 68 | } 69 | } 70 | 71 | main(); 72 | -------------------------------------------------------------------------------- /.bit/tests/test.1.8.js: -------------------------------------------------------------------------------- 1 | 2 | let uri; 3 | const fetch = require('node-fetch'); 4 | const args = require('minimist')(process.argv.slice(2)) 5 | const functions = require('./functions'); 6 | const user = args['user']; 7 | const repo = args['repo']; 8 | 9 | async function main() { 10 | try { 11 | uri = process.env.MORSE_ENDPOINT 12 | functions.checkSecret(uri, "MORSE_ENDPOINT") 13 | 14 | 15 | const answer = ".. .-.. --- ...- . -... .. - .--. .-. --- .--- . -.-. -" 16 | 17 | uri = functions.queryString(uri) 18 | 19 | try { 20 | const resp = await fetch(uri + "&plaintext=ilovebitproject", { 21 | method: 'GET' 22 | }); 23 | var data = await resp.text() 24 | let test = JSON.stringify(data) 25 | functions.validateResponseStatus(resp, uri) 26 | } catch (e) { 27 | console.error("We're having trouble making a request to your endpoint. Try again?") 28 | await functions.throwError("We're having trouble making a request to your endpoint. Try again?", user, repo) 29 | process.exit(1) 30 | } 31 | 32 | if (data.length < 3) { 33 | console.error("No response... Try again!") 34 | await functions.throwError("No response... Try again!", user, repo) 35 | process.exit(1) 36 | } else if (data == answer) { 37 | console.info("Yay!🎉 Success - thanks for helping us on this top secret mission. Welcome to the team.") 38 | console.info(`We got "${answer}" with the input of ilovebitproject`) 39 | } else { 40 | console.error(`YIKES! We got "${data}" instead of "${answer}". Try again!`) 41 | await functions.throwError(`YIKES! We got '${data}' instead of '${answer}'. Try again!`, user, repo) 42 | process.exit(1) 43 | } 44 | 45 | 46 | try { 47 | const resp2 = await fetch(uri, { 48 | method: 'GET' 49 | }); 50 | var data2 = await resp2.text() 51 | functions.validateResponseStatus(resp2, uri) 52 | } catch (e) { 53 | console.error("We're having trouble making a request to your endpoint when plaintext is blank. Try again?") 54 | await functions.throwError("We're having trouble making a request to your endpoint when plaintext is blank. Try again?", user, repo) 55 | process.exit(1) 56 | } 57 | 58 | if (data2.length < 3) { 59 | console.error("No response... Try again!") 60 | await functions.throwError(e, user, repo) 61 | process.exit(1) 62 | } else if (data2 == "Please enter some text to convert!") { 63 | console.info("Also, great work catching a blank plaintext parameter value.") 64 | } else { 65 | console.error(`Sorry! You forgot to check for a blank plaintext. If we sending nothing in "plaintext," we should get "Please enter some text to convert!" Instead, we got "${data2}"`) 66 | await functions.throwError(`Sorry! You forgot to check for a blank plaintext. If we sending nothing in 'plaintext', we should get 'Please enter some text to convert!' Instead, we got '${data2}'`, user, repo) 67 | process.exit(1) 68 | } 69 | 70 | 71 | } 72 | catch (e) { 73 | await functions.throwError(e, user, repo) 74 | } 75 | } 76 | 77 | main(); 78 | 79 | 80 | -------------------------------------------------------------------------------- /.bit/tests/test.2.2.js: -------------------------------------------------------------------------------- 1 | let uri = undefined 2 | const fetch = require('node-fetch'); 3 | const args = require('minimist')(process.argv.slice(2)) 4 | const user = args['user']; 5 | const repo = args['repo']; 6 | const fs = require('fs'); 7 | const FormData = require('form-data'); 8 | const functions = require('./functions.js') 9 | 10 | 11 | async function main() { 12 | try { 13 | uri = process.env.EMOTIONAL_ENDPOINT 14 | 15 | functions.checkSecret(uri, "EMOTIONAL_ENDPOINT") 16 | 17 | // if you wanna add more files, just put a comma after the filename(array) 18 | const commit_file = ['emotionalgifs/index.js'] 19 | 20 | 21 | functions.checkCommit(commit_file) 22 | 23 | fs.readFile(`${__dirname}/testimage.jpg`, async function (err, content) { 24 | try { 25 | let formData = new FormData() 26 | formData.append('data', content, { filename: "testimage.jpeg", type: "image/jpeg", data: content }) 27 | 28 | const formHeaders = formData.getHeaders(); 29 | var baseImage = Buffer.from(content).toString('base64') 30 | const resp = await fetch(uri, { 31 | method: 'POST', 32 | body: formData, 33 | headers: { 34 | ...formHeaders, 35 | }, 36 | }); 37 | var result = await resp.text() 38 | let test = JSON.stringify(result) 39 | 40 | functions.validateResponseStatus(resp, uri) 41 | 42 | if (test.length < 3) { 43 | console.error("No response... Try again!") 44 | await functions.throwError(e, user, repo) 45 | process.exit(1) 46 | } else if (result != baseImage) { 47 | console.error("Sorry, we didn't get our image back in base64. Try again!") 48 | await functions.throwError("Sorry, we didn't get our image back in base64. Try again!", user, repo) 49 | process.exit(1) 50 | } else { 51 | console.info("Yay! 🎉 We got the same image back in base64!") 52 | } 53 | } catch (e) { 54 | console.error("Try again! We got this error when trying to make a request: " + e) 55 | await functions.throwError("Try again! We got this error when trying to make a request: " + e, user, repo) 56 | process.exit(1) 57 | } 58 | }) 59 | 60 | 61 | } 62 | catch (e) { 63 | await functions.throwError(e, user, repo) 64 | } 65 | } 66 | 67 | 68 | main(); 69 | -------------------------------------------------------------------------------- /.bit/tests/test.2.3.js: -------------------------------------------------------------------------------- 1 | let uri = undefined 2 | const fetch = require('node-fetch'); 3 | const fs = require('fs'); 4 | const FormData = require('form-data'); 5 | const functions = require('./functions.js') 6 | const args = require('minimist')(process.argv.slice(2)) 7 | const user = args['user']; 8 | const repo = args['repo']; 9 | 10 | 11 | async function main() { 12 | try { 13 | uri = process.env.EMOTIONAL_ENDPOINT 14 | 15 | functions.checkSecret(uri, "EMOTIONAL_ENDPOINT") 16 | 17 | 18 | fs.readFile(`${__dirname}/testimage.jpg`, async function (err, content) { 19 | try { 20 | let formData = new FormData() 21 | formData.append('data', content, { filename: "testimage.jpeg", type: "image/jpeg", data: content }) 22 | 23 | const formHeaders = formData.getHeaders(); 24 | 25 | const resp = await fetch(uri, { 26 | method: 'POST', 27 | body: formData, 28 | headers: { 29 | ...formHeaders, 30 | }, 31 | }); 32 | var result = await resp.json() 33 | let test = JSON.stringify(result) 34 | 35 | functions.validateResponseStatus(resp, uri) 36 | 37 | if (test.length < 3) { 38 | console.error("No response... Try again!") 39 | await functions.throwError("No response... Try again!", user, repo) 40 | process.exit(1) 41 | } else if (result.result[0].faceAttributes.emotion.happiness == 1) { 42 | console.info("Yay! 🎉 We got the happiness as: " + result.result[0].faceAttributes.emotion.happiness) 43 | } else { 44 | console.error("Try again! We didn't get an emotion back. Make sure you are returning in JSON format.") 45 | console.error(`We got "${result}", and could not get happiness from the JSON object.`) 46 | await functions.throwError(`We got '${result}', and could not get happiness from the JSON object.`, user, repo) 47 | process.exit(1) 48 | } 49 | } catch (e) { 50 | console.error("Try again! We got this error when trying to make a request: " + e) 51 | await functions.throwError("Try again! We got this error when trying to make a request: " + e, user, repo) 52 | process.exit(1) 53 | } 54 | }) 55 | 56 | 57 | 58 | } 59 | catch (e) { 60 | await functions.throwError(e, user, repo) 61 | } 62 | } 63 | 64 | 65 | 66 | 67 | main(); 68 | -------------------------------------------------------------------------------- /.bit/tests/test.2.4.js: -------------------------------------------------------------------------------- 1 | let uri = undefined 2 | const fetch = require('node-fetch'); 3 | const fs = require('fs'); 4 | const FormData = require('form-data'); 5 | const functions = require('./functions.js') 6 | const args = require('minimist')(process.argv.slice(2)) 7 | const user = args['user']; 8 | const repo = args['repo']; 9 | 10 | 11 | async function main() { 12 | try { 13 | uri = process.env.EMOTIONAL_ENDPOINT 14 | 15 | functions.checkSecret(uri, "EMOTIONAL_ENDPOINT") 16 | 17 | 18 | 19 | fs.readFile(`${__dirname}/testimage.jpg`, async function (err, content) { 20 | try { 21 | let formData = new FormData() 22 | formData.append('data', content, { filename: "testimage.jpeg", type: "image/jpeg", data: content }) 23 | 24 | const formHeaders = formData.getHeaders(); 25 | 26 | const resp = await fetch(uri, { 27 | method: 'POST', 28 | body: formData, 29 | headers: { 30 | ...formHeaders, 31 | }, 32 | }); 33 | var result = await resp.text() 34 | let test = JSON.stringify(result) 35 | 36 | functions.validateResponseStatus(resp, uri) 37 | 38 | if (test.length < 3) { 39 | console.error("No response... Try again!") 40 | await functions.throwError("No response... Try again!", user, repo) 41 | process.exit(1) 42 | } else if (result == "happiness") { 43 | console.info("Yay! 🎉 You're right, we're super happy (that was the main emotion in our test picture)") 44 | } else { 45 | console.error("Try again! We didn't get the correct main emotion back.") 46 | console.error(`We got "${result}" instead.`) 47 | await functions.throwError(`Try again! We didn't get the correct main emotion back. We got '${result}' instead.`, user, repo) 48 | process.exit(1) 49 | } 50 | } catch (e) { 51 | console.error("Try again! We got this error when trying to make a request: " + e) 52 | await functions.throwError("Try again! We got this error when trying to make a request: " + e, user, repo) 53 | process.exit(1) 54 | } 55 | }) 56 | 57 | 58 | } 59 | catch (e) { 60 | await functions.throwError(e, user, repo) 61 | } 62 | } 63 | 64 | 65 | 66 | main(); 67 | -------------------------------------------------------------------------------- /.bit/tests/test.2.5.js: -------------------------------------------------------------------------------- 1 | 2 | let uri = undefined 3 | const fetch = require('node-fetch'); 4 | const fs = require('fs'); 5 | const FormData = require('form-data'); 6 | const functions = require('./functions.js') 7 | const args = require('minimist')(process.argv.slice(2)) 8 | const user = args['user']; 9 | const repo = args['repo']; 10 | 11 | 12 | async function main() { 13 | try { 14 | uri = process.env.EMOTIONAL_ENDPOINT 15 | 16 | functions.checkSecret(uri, "EMOTIONAL_ENDPOINT") 17 | 18 | fs.readFile(`${__dirname}/testimage.jpg`, async function (err, content) { 19 | try { 20 | let formData = new FormData() 21 | formData.append('data', content, { filename: "testimage.jpeg", type: "image/jpeg", data: content }) 22 | 23 | const formHeaders = formData.getHeaders(); 24 | 25 | const resp = await fetch(uri, { 26 | method: 'POST', 27 | body: formData, 28 | headers: { 29 | ...formHeaders, 30 | }, 31 | }); 32 | var result = await resp.text() 33 | let test = JSON.stringify(result) 34 | 35 | functions.validateResponseStatus(resp, uri) 36 | 37 | if (test.length < 3) { 38 | console.error("No response... Try again!") 39 | await functions.throwError("No response... Try again!", user, repo) 40 | process.exit(1) 41 | } else if (result.includes("https://giphy.com/gifs/")) { 42 | console.info("Yay! 🎉 Thanks for the gif! " + result) 43 | } else { 44 | console.error("Try again! We didn't get a link to a gif back. We got " + result + " instead.") 45 | await functions.throwError("Try again! We didn't get a link to a gif back. We got " + result + " instead.", user, repo) 46 | process.exit(1) 47 | } 48 | } catch (e) { 49 | console.error("Try again! We got this error when trying to make a request: " + e) 50 | await functions.throwError("Try again! We got this error when trying to make a request: " + e, user, repo) 51 | process.exit(1) 52 | } 53 | }) 54 | 55 | 56 | } 57 | catch (e) { 58 | await functions.throwError(e, user, repo) 59 | } 60 | } 61 | 62 | main(); 63 | -------------------------------------------------------------------------------- /.bit/tests/test.2.6.js: -------------------------------------------------------------------------------- 1 | let uri = undefined 2 | const fetch = require('node-fetch'); 3 | const functions = require('./functions.js') 4 | const args = require('minimist')(process.argv.slice(2)) 5 | const user = args['user']; 6 | const repo = args['repo']; 7 | 8 | 9 | async function main() { 10 | try { 11 | uri = process.env.SONGREC_ENDPOINT 12 | 13 | functions.checkSecret(uri, "SONGREC_ENDPOINT") 14 | 15 | 16 | const resp = await fetch(uri, { 17 | method: 'POST', 18 | body: "ToCountry=US&MediaContentType0=image%2Fjpeg&ToState=MI&SmsMessageSid=MM0fe83458b74a1f626eb0da4685ab28b5&NumMedia=1&ToCity=UTICA&FromZip=28394&SmsSid=MM0fe83458b74a1f626eb0da4685ab28b5&FromState=NC&SmsStatus=received&FromCity=VASS&Body=&FromCountry=US&To=%2B15869913930&ToZip=48316&NumSegments=1&MessageSid=MM0fe83458b74a1f626eb0da4685ab28b5&AccountSid=ACee62fed677d382600b621e6f24de9bb0&From=%2B19105563874&MediaUrl0=https%3A%2F%2Fapi.twilio.com%2F2010-04-01%2FAccounts%2FACee62fed677d382600b621e6f24de9bb0%2FMessages%2FMM0fe83458b74a1f626eb0da4685ab28b5%2FMedia%2FME29644fd97901859108bc35e210b588f6&ApiVersion=2010-04-01" 19 | }); 20 | var result = await resp.text() 21 | let test = JSON.stringify(result) 22 | 23 | functions.validateResponseStatus(resp, uri) 24 | 25 | if (test.length < 3) { 26 | console.error("No response... Try again!") 27 | await functions.throwError("No response... Try again!", user, repo) 28 | process.exit(1) 29 | } else if (result == "https://api.twilio.com/2010-04-01/Accounts/ACee62fed677d382600b621e6f24de9bb0/Messages/MM0fe83458b74a1f626eb0da4685ab28b5/Media/ME29644fd97901859108bc35e210b588f6") { 30 | console.info("Yay! 🎉 Thanks for the image link!") 31 | } else { 32 | console.error("Try again! We didn't get the image link back.") 33 | console.error(`We got "${result}" instead.`) 34 | await functions.throwError(`Try again! We didn't get the image link back. We got '${result}' instead.`, user, repo) 35 | process.exit(1) 36 | } 37 | 38 | } 39 | catch (e) { 40 | console.log("ERROR: ") 41 | await functions.throwError(e, user, repo) 42 | } 43 | } 44 | 45 | 46 | 47 | main(); 48 | -------------------------------------------------------------------------------- /.bit/tests/test.2.7.js: -------------------------------------------------------------------------------- 1 | let uri = undefined 2 | const fetch = require('node-fetch'); 3 | const functions = require('./functions.js') 4 | const args = require('minimist')(process.argv.slice(2)) 5 | const user = args['user']; 6 | const repo = args['repo']; 7 | 8 | 9 | 10 | async function main() { 11 | try { 12 | 13 | uri = process.env.SONGREC_ENDPOINT 14 | 15 | functions.checkSecret(uri, "SONGREC_ENDPOINT") 16 | 17 | 18 | const resp = await fetch(uri, { 19 | method: 'POST', 20 | body: "ToCountry=US&MediaContentType0=image%2Fjpeg&ToState=MI&SmsMessageSid=MM0fe83458b74a1f626eb0da4685ab28b5&NumMedia=1&ToCity=UTICA&FromZip=28394&SmsSid=MM0fe83458b74a1f626eb0da4685ab28b5&FromState=NC&SmsStatus=received&FromCity=VASS&Body=&FromCountry=US&To=%2B15869913930&ToZip=48316&NumSegments=1&MessageSid=MM0fe83458b74a1f626eb0da4685ab28b5&AccountSid=ACee62fed677d382600b621e6f24de9bb0&From=%2B19105563874&MediaUrl0=https%3A%2F%2Fapi.twilio.com%2F2010-04-01%2FAccounts%2FACee62fed677d382600b621e6f24de9bb0%2FMessages%2FMM0fe83458b74a1f626eb0da4685ab28b5%2FMedia%2FME29644fd97901859108bc35e210b588f6&ApiVersion=2010-04-01" 21 | }); 22 | 23 | functions.validateResponseStatus(resp, uri) 24 | 25 | var result = await resp.text() 26 | let test = JSON.stringify(result) 27 | 28 | if (test.length < 3) { 29 | console.error("No response... Try again!") 30 | await functions.throwError("No response... Try again!", user, repo) 31 | process.exit(1) 32 | } else if (result == "GenY") { 33 | console.info("Yay! 🎉 You're right, you guessed the generation correctly!") 34 | } else { 35 | console.error("Try again! We didn't get the correct generation/age back.") 36 | console.error(`We got "${result}" but expected "GenY"`) 37 | await functions.throwError(`Try again! We didn't get the correct generation/age back. We got '${result}' but expected 'GenY'`, user, repo) 38 | process.exit(1) 39 | } 40 | } 41 | catch (e) { 42 | await functions.throwError(e, user, repo) 43 | } 44 | } 45 | 46 | 47 | 48 | 49 | 50 | main(); 51 | -------------------------------------------------------------------------------- /.bit/tests/test.2.8.js: -------------------------------------------------------------------------------- 1 | let uri = undefined 2 | const fetch = require('node-fetch'); 3 | const functions = require('./functions.js') 4 | const args = require('minimist')(process.argv.slice(2)) 5 | const user = args['user']; 6 | const repo = args['repo']; 7 | 8 | 9 | async function main() { 10 | try { 11 | uri = process.env.SONGREC_ENDPOINT 12 | 13 | functions.checkSecret(uri, "SONGREC_ENDPOINT") 14 | 15 | const resp = await fetch(uri, { 16 | method: 'POST', 17 | body: "ToCountry=US&MediaContentType0=image%2Fjpeg&ToState=MI&SmsMessageSid=MM0fe83458b74a1f626eb0da4685ab28b5&NumMedia=1&ToCity=UTICA&FromZip=28394&SmsSid=MM0fe83458b74a1f626eb0da4685ab28b5&FromState=NC&SmsStatus=received&FromCity=VASS&Body=&FromCountry=US&To=%2B15869913930&ToZip=48316&NumSegments=1&MessageSid=MM0fe83458b74a1f626eb0da4685ab28b5&AccountSid=ACee62fed677d382600b621e6f24de9bb0&From=%2B19105563874&MediaUrl0=https%3A%2F%2Fapi.twilio.com%2F2010-04-01%2FAccounts%2FACee62fed677d382600b621e6f24de9bb0%2FMessages%2FMM0fe83458b74a1f626eb0da4685ab28b5%2FMedia%2FME29644fd97901859108bc35e210b588f6&ApiVersion=2010-04-01" 18 | }); 19 | var result = await resp.text() 20 | let test = JSON.stringify(result) 21 | 22 | functions.validateResponseStatus(resp, uri) 23 | 24 | if (test.length < 3) { 25 | console.error("No response... Try again!") 26 | await functions.throwError("No response... Try again!", user, repo) 27 | process.exit(1) 28 | } else if (result == "We guessed you're part of this generation: GenY! Happy listening! https://open.spotify.com/track/1Je1IMUlBXcx1Fz0WE7oPT?si=a04bbdf6ec4948b9") { 29 | console.info("Yay! 🎉 You're right, you guessed the generation correctly AND returned the right song!") 30 | } else { 31 | console.error(`Try again! We received "${result}" instead of the correct response, "We guessed you're part of this generation: GenY! Happy listening! https://open.spotify.com/track/1Je1IMUlBXcx1Fz0WE7oPT?si=a04bbdf6ec4948b9".`) 32 | await functions.throwError(`Try again! We received '${result}' instead of the correct response, 'We guessed you're part of this generation: GenY! Happy listening! https://open.spotify.com/track/1Je1IMUlBXcx1Fz0WE7oPT?si=a04bbdf6ec4948b9'.`, user, repo) 33 | process.exit(1) 34 | } 35 | 36 | } 37 | catch (e) { 38 | await functions.throwError(e, user, repo) 39 | } 40 | } 41 | 42 | 43 | 44 | 45 | 46 | main(); 47 | -------------------------------------------------------------------------------- /.bit/tests/test.3.1.js: -------------------------------------------------------------------------------- 1 | const connectionString = process.env.storage_account_connection_string; 2 | const containerName = process.env.container_name; 3 | const { BlobServiceClient } = require("@azure/storage-blob"); 4 | const fs = require("fs") 5 | const functions = require('./functions.js') 6 | const args = require('minimist')(process.argv.slice(2)) 7 | const user = args['user']; 8 | const repo = args['repo']; 9 | 10 | 11 | async function main() { 12 | try { 13 | 14 | functions.checkSecret(connectionString, "connectionString") 15 | functions.checkSecret(containerName, "containerName") 16 | 17 | fs.readFile(`${__dirname}/testimage.jpg`, async function (err, content) { 18 | try { 19 | const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString); 20 | // Create a unique name for the container 21 | console.error('\nCreating container...'); 22 | console.error('\t', containerName); 23 | 24 | // Get a reference to a container 25 | const containerClient = blobServiceClient.getContainerClient(containerName); 26 | 27 | // Create the container 28 | const blobName = 'bitcamptest.jpg'; 29 | 30 | // Get a block blob client 31 | const blockBlobClient = containerClient.getBlockBlobClient(blobName); 32 | 33 | console.error('\nUploading to Azure storage as blob:\n\t', blobName); 34 | 35 | // Upload data to the blob 36 | const uploadBlobResponse = await blockBlobClient.upload(content, content.length); 37 | } catch (e) { 38 | console.error("Sorry! You haven't created your blob storage account yet. Check your secrets!") 39 | await functions.throwError(`Error with creating blob storage account, check your secrets! Here is the error: ${e}`, user, repo) 40 | process.exit(1) 41 | } 42 | }) 43 | 44 | } 45 | catch (e) { 46 | await functions.throwError(e, user, repo) 47 | } 48 | } 49 | 50 | main(); 51 | -------------------------------------------------------------------------------- /.bit/tests/test.3.2.js: -------------------------------------------------------------------------------- 1 | let uri = undefined 2 | const fetch = require('node-fetch'); 3 | const fs = require('fs'); 4 | const FormData = require('form-data'); 5 | const functions = require('./functions.js') 6 | 7 | uri = process.env.BUNNIMAGE_ENDPOINT 8 | const blob_url = process.env.blob_url 9 | const containerName = process.env.container_name 10 | 11 | const args = require('minimist')(process.argv.slice(2)) 12 | const user = args['user']; 13 | const repo = args['repo']; 14 | 15 | 16 | 17 | async function main() { 18 | try { 19 | 20 | functions.checkSecret(blob_url, "blob_url") 21 | functions.checkSecret(containerName, "containerName") 22 | 23 | 24 | fs.readFile(`${__dirname}/testimage.jpg`, async function (err, content) { 25 | try { 26 | let formData = new FormData() 27 | formData.append('data', content, { filename: "testimage.jpeg", type: "image/jpeg", data: content }) 28 | 29 | const formHeaders = formData.getHeaders(); 30 | 31 | const resp1 = await fetch(uri, { 32 | method: 'POST', 33 | body: formData, 34 | headers: { 35 | ...formHeaders, 36 | }, 37 | }); 38 | var result1 = await resp1.text() 39 | let test1 = JSON.stringify(result1) 40 | 41 | functions.validateResponseStatus(resp1, uri) 42 | 43 | var download = `${blob_url}/${containerName}/test.jpeg`; 44 | 45 | let resp = await fetch(download, { 46 | method: 'GET', 47 | }) 48 | let data = await resp; 49 | if (data.statusText == "The specified blob does not exist.") { 50 | console.error("Hmm... We couldn't find our image. Try again?") 51 | console.error(`We tried using "${download}" to find the image, but did not receive a response.`) 52 | await functions.throwError(`Hmm... We couldn't find our image. Try again? We tried using '${download}' to find the image, but did not receive a response.`, user, repo) 53 | process.exit(1) 54 | } else { 55 | console.info("Yay! 🎉 We got our picture!") 56 | } 57 | } catch (e) { 58 | console.error("Try again! We got this error when trying to make a request: " + e) 59 | await functions.throwError("Try again! We got this error when trying to make a request: " + e, user, repo) 60 | process.exit(1) 61 | } 62 | }) 63 | } 64 | catch (e) { 65 | await functions.throwError(e, user, repo) 66 | } 67 | } 68 | 69 | main(); 70 | -------------------------------------------------------------------------------- /.bit/tests/test.3.3.js: -------------------------------------------------------------------------------- 1 | let uri = undefined 2 | const fetch = require('node-fetch'); 3 | const fs = require('fs'); 4 | const FormData = require('form-data'); 5 | const functions = require('./functions.js') 6 | 7 | const args = require('minimist')(process.argv.slice(2)) 8 | const user = args['user']; 9 | const repo = args['repo']; 10 | 11 | 12 | async function main() { 13 | try { 14 | uri = process.env.BUNNIMAGE_ENDPOINT 15 | const blob_url = process.env.blob_url 16 | const containerName = process.env.container_name 17 | 18 | functions.checkSecret(uri, "BUNNIMAGE_ENDPOINT") 19 | 20 | fs.readFile(`${__dirname}/testimage.jpg`, async function (err, content) { 21 | try { 22 | let formData = new FormData() 23 | formData.append('data', content, { filename: "testimage.jpeg", type: "image/jpeg", data: content }) 24 | 25 | const formHeaders = formData.getHeaders(); 26 | 27 | const resp1 = await fetch(uri, { 28 | method: 'POST', 29 | body: formData, 30 | headers: { 31 | ...formHeaders, 32 | "codename": "bunnimagetestrun1000" 33 | }, 34 | }); 35 | var result1 = await resp1.text() 36 | let test1 = JSON.stringify(result1) 37 | 38 | functions.validateResponseStatus(resp1, uri) 39 | 40 | var download = `${blob_url}/${containerName}/bunnimagetestrun1000.jpeg`; 41 | 42 | let resp = await fetch(download, { 43 | method: 'GET', 44 | }) 45 | let data = await resp; 46 | if (data.statusText == "The specified blob does not exist.") { 47 | console.error("Hmm... We couldn't find our image with the correct name. Try again?") 48 | await functions.throwError("Hmm... We couldn't find our image with the correct name. Try again?", user, repo) 49 | process.exit(1) 50 | } else { 51 | console.info("Yay! 🎉 We got our picture!") 52 | } 53 | 54 | const testresp = await fetch(uri, { 55 | method: 'POST', 56 | body: "" 57 | }); 58 | var message = await testresp.text() 59 | 60 | if (message == "Sorry! No image attached.") { 61 | console.error("Nice job catching those exceptions.") 62 | } else { 63 | console.error("You missed something. What if someone didn't submit an image in the body?") 64 | await functions.throwError("You missed something. What if someone didn't submit an image in the body?", user, repo) 65 | process.exit(1) 66 | } 67 | } catch (e) { 68 | console.error("Try again! We got this error when trying to make a request: " + e) 69 | await functions.throwError("Try again! We got this error when trying to make a request: " + e, user, repo) 70 | process.exit(1) 71 | } 72 | }) 73 | } 74 | catch (e) { 75 | await functions.throwError(e, user, repo) 76 | } 77 | } 78 | 79 | main(); 80 | -------------------------------------------------------------------------------- /.bit/tests/test.3.5.js: -------------------------------------------------------------------------------- 1 | let uri1 = undefined 2 | let uri2 = undefined 3 | const fetch = require('node-fetch'); 4 | const fs = require('fs'); 5 | const FormData = require('form-data'); 6 | const functions = require('./functions.js') 7 | 8 | const args = require('minimist')(process.argv.slice(2)) 9 | const user = args['user']; 10 | const repo = args['repo']; 11 | 12 | async function main() { 13 | try { 14 | uri1 = process.env.BUNNIMAGE_ENDPOINT 15 | uri2 = process.env.BUNNIMAGE_ENDPOINT2 16 | const blob_url = process.env.blob_url 17 | const containerName = process.env.container_name 18 | functions.checkSecret(uri2, "BUNNIMAGE_ENDPOINT2") 19 | fs.readFile(`${__dirname}/testimage.jpg`, async function (err, content) { 20 | try { 21 | let formData = new FormData() 22 | formData.append('data', content, { filename: "testimage.jpeg", type: "image/jpeg", data: content }) 23 | 24 | const formHeaders = formData.getHeaders(); 25 | 26 | const resp1 = await fetch(uri1, { 27 | method: 'POST', 28 | body: formData, 29 | headers: { 30 | ...formHeaders, 31 | "codename": "bunnimagetestrun1001" 32 | }, 33 | }); 34 | var result1 = await resp1.text() 35 | let test1 = JSON.stringify(result1) 36 | 37 | const testresp = await fetch(uri2, { 38 | method: 'GET', 39 | headers: { 40 | "username": "bunnimagetestrun1001" 41 | } 42 | }); 43 | var message = await testresp.json() 44 | 45 | functions.validateResponseStatus(testresp, uri) 46 | 47 | if (JSON.stringify(message.downloadUri).includes(`${blob_url}/${containerName}/bunnimagetestrun1001.jpeg`)) { 48 | console.info("Yay! 🎉 We got our picture!") 49 | } else { 50 | console.error("Hmmm... Maybe take another look at that download function.") 51 | console.error("Check to make sure you don't have extra characters in your secrets, and test your function yourself as well.") 52 | await functions.throwError("Hmmm... Maybe take another look at that download function. Also, check to make sure you don't have extra characters in your secrets, and test your function yourself as well.", user, repo) 53 | process.exit(1) 54 | } 55 | } catch (e) { 56 | console.error("Try again! We got this error when trying to make a request: " + e) 57 | await functions.throwError("Try again! We got this error when trying to make a request: " + e, user, repo) 58 | process.exit(1) 59 | } 60 | }) 61 | 62 | } 63 | catch (e) { 64 | await functions.throwError(e, user, repo) 65 | } 66 | } 67 | 68 | 69 | 70 | 71 | 72 | 73 | main(); 74 | -------------------------------------------------------------------------------- /.bit/tests/test.3.6.js: -------------------------------------------------------------------------------- 1 | let uri = undefined 2 | const fetch = require('node-fetch'); 3 | const functions = require('./functions.js') 4 | const args = require('minimist')(process.argv.slice(2)) 5 | const user = args['user']; 6 | const repo = args['repo']; 7 | 8 | async function main() { 9 | try { 10 | uri = process.env.DEEPSECRETS_ENDPOINT 11 | 12 | functions.checkSecret(uri, "DEEPSECRETS_ENDPOINT") 13 | 14 | const resp = await fetch(uri, { 15 | method: 'POST', 16 | body: "ToCountry=US&MediaContentType0=image%2Fjpeg&ToState=MI&SmsMessageSid=MM0fe83458b74a1f626eb0da4685ab28b5&NumMedia=1&ToCity=UTICA&FromZip=28394&SmsSid=MM0fe83458b74a1f626eb0da4685ab28b5&FromState=NC&SmsStatus=received&FromCity=VASS&Body=Hi&FromCountry=US&To=%2B15869913930&ToZip=48316&NumSegments=1&MessageSid=MM0fe83458b74a1f626eb0da4685ab28b5&AccountSid=ACee62fed677d382600b621e6f24de9bb0&From=%2B19105563874&MediaUrl0=https%3A%2F%2Fapi.twilio.com%2F2010-04-01%2FAccounts%2FACee62fed677d382600b621e6f24de9bb0%2FMessages%2FMM0fe83458b74a1f626eb0da4685ab28b5%2FMedia%2FME29644fd97901859108bc35e210b588f6&ApiVersion=2010-04-01" 17 | }); 18 | var result = await resp.text() 19 | let test = JSON.stringify(result) 20 | 21 | functions.validateResponseStatus(resp, uri) 22 | 23 | if (test.length < 3) { 24 | console.error("No response... Try again!") 25 | await functions.throwError("No response... Try again!", user, repo) 26 | process.exit(1) 27 | } else if (result == "Hi") { 28 | console.info("Yay! 🎉 Thanks for returning our message!") 29 | } else { 30 | console.error("Try again! We didn't get our message back.") 31 | console.error(`We got "${result}" instead of "Hi", which is what we sent you.`) 32 | await functions.throwError(`Try again! We didn't get our message back. We got '${result}' instead of 'Hi', which is what we sent you.`, user, repo) 33 | process.exit(1) 34 | } 35 | 36 | } 37 | catch (e) { 38 | await functions.throwError(e, user, repo) 39 | } 40 | } 41 | 42 | main(); 43 | -------------------------------------------------------------------------------- /.bit/tests/test.3.7.js: -------------------------------------------------------------------------------- 1 | let uri = undefined 2 | const fetch = require('node-fetch'); 3 | const functions = require('./functions.js') 4 | const args = require('minimist')(process.argv.slice(2)) 5 | const user = args['user']; 6 | const repo = args['repo']; 7 | 8 | async function main() { 9 | try { 10 | uri = process.env.DEEPSECRETS_ENDPOINT 11 | 12 | functions.checkSecret(uri, "DEEPSECRETS_ENDPOINT") 13 | 14 | const resp1 = await fetch(uri, { 15 | method: 'POST', 16 | body: "Body=testmessage2" 17 | }); 18 | var result1 = await resp1.text() 19 | 20 | functions.validateResponseStatus(resp1, uri) 21 | 22 | const resp = await fetch(uri, { 23 | method: 'POST', 24 | body: "Body=testmessage3" 25 | }); 26 | var result = await resp.text() 27 | let test = JSON.stringify(result) 28 | 29 | if (test.length < 3) { 30 | console.error("No response... Try again!") 31 | await functions.throwError("No response... Try again!", user, repo) 32 | process.exit(1) 33 | } else if (result == `Thanks 😊! Stored your secret "testmessage3". 😯 Someone confessed that: "testmessage2"`) { 34 | console.info("Yay! 🎉 Thanks for returning our message from your database!") 35 | } else { 36 | console.error("Try again! We didn't get our most recent secret back.") 37 | console.error(`We got "${result}", which is incorrect.`) 38 | await functions.throwError(`Try again! We didn't get our most recent secret back. We got '${result}', which is incorrect.`, user, repo) 39 | process.exit(1) 40 | } 41 | 42 | 43 | } 44 | catch (e) { 45 | await functions.throwError(e, user, repo) 46 | } 47 | } 48 | 49 | main(); 50 | -------------------------------------------------------------------------------- /.bit/tests/test.3.8.js: -------------------------------------------------------------------------------- 1 | let uri = undefined 2 | const fetch = require('node-fetch'); 3 | const functions = require('./functions.js') 4 | const args = require('minimist')(process.argv.slice(2)) 5 | const user = args['user']; 6 | const repo = args['repo']; 7 | 8 | async function main() { 9 | try { 10 | uri = process.env.DEEPSECRETS_ENDPOINT 11 | 12 | functions.checkSecret(uri, "DEEPSECRETS_ENDPOINT") 13 | 14 | const resp1 = await fetch(uri, { 15 | method: 'POST', 16 | body: "Body=testmessage2" 17 | }); 18 | var result1 = await resp1.text() 19 | 20 | functions.validateResponseStatus(resp1, uri) 21 | 22 | const resp = await fetch(uri, { 23 | method: 'POST', 24 | body: "Body=testmessage3" 25 | }); 26 | var result = await resp.text() 27 | let test = JSON.stringify(result) 28 | 29 | if (test.length < 3) { 30 | console.error("No response... Try again!") 31 | await functions.throwError("No response... Try again!", user, repo) 32 | process.exit(1) 33 | } else if (result != `Thanks 😊! Stored your secret "testmessage3". 😯 Someone confessed that: "testmessage2"`) { 34 | console.info("Yay! 🎉 Thanks for returning a random message!") 35 | } else { 36 | console.error("Hey... Make sure it's random!.") 37 | console.error(`We got "${result}", which proves it was not random.`) 38 | await functions.throwError(`Hey... Make sure it's random!. We got '${result}', which proves it was not random.`, user, repo) 39 | process.exit(1) 40 | } 41 | } 42 | catch (e) { 43 | await functions.throwError(e, user, repo) 44 | } 45 | } 46 | 47 | 48 | main(); 49 | -------------------------------------------------------------------------------- /.bit/tests/test.4.6.js: -------------------------------------------------------------------------------- 1 | let uri = undefined 2 | const fetch = require('node-fetch'); 3 | const functions = require('./functions.js') 4 | 5 | const args = require('minimist')(process.argv.slice(2)) 6 | const user = args['user']; 7 | const repo = args['repo']; 8 | 9 | 10 | async function main() { 11 | try { 12 | 13 | uri = process.env.TWOCATZ_ENDPOINT 14 | 15 | functions.checkSecret(uri, "TWOCATZ_ENDPOINT") 16 | 17 | uri = functions.queryString(uri) 18 | const resp = await fetch(uri + "&name1=hi&name2=hi&name3=hi&name4=hi", { 19 | method: 'GET' 20 | }); 21 | var data = await resp.json() 22 | let test = JSON.stringify(data) 23 | 24 | functions.validateResponseStatus(resp, uri) 25 | 26 | if (test.length < 3) { 27 | console.error("No response... Try again!") 28 | await functions.throwError("No response... Try again!", user, repo) 29 | process.exit(1) 30 | } 31 | 32 | try { 33 | var catimage1 = data.cat1; 34 | var catimage2 = data.cat2; 35 | var catimage3 = data.cat3; 36 | var catimage4 = data.cat4; 37 | console.info("Yay! 🎉 We got your cat pictures 🐱") 38 | } catch (e) { 39 | console.error("Sorry! We couldn't find one or both of the cat pictures. Make sure you encoded in BASE64!") 40 | await functions.throwError("Sorry! We couldn't find one or both of the cat pictures. Make sure you encoded in BASE64!", user, repo) 41 | process.exit(1) 42 | } 43 | } 44 | catch (e) { 45 | await functions.throwError(e, user, repo) 46 | } 47 | } 48 | 49 | main(); 50 | -------------------------------------------------------------------------------- /.bit/tests/testimage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitprj/Intro-To-Serverless/7fd8024b8d259eb4772785db540bc0e01f34c3c1/.bit/tests/testimage.jpg -------------------------------------------------------------------------------- /.bit/tests/wrongbranch.js: -------------------------------------------------------------------------------- 1 | console.error("Whoops! 😬 You're on the wrong branch. Make sure to create a new one every week!") 2 | // process.exit(1) 3 | -------------------------------------------------------------------------------- /.bit/workflows/bunnimage-frontend.yml: -------------------------------------------------------------------------------- 1 | name: Bunnimage Frontend 2 | on: 3 | push: 4 | branches: 5 | - bunnimage-frontend 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v2 14 | 15 | - name: Setup Node Environment 16 | uses: actions/setup-node@v2 17 | with: 18 | node-version: '14' 19 | 20 | - name: Install Dependencies 21 | run: | 22 | npm install minimist 23 | npm install node-fetch 24 | 25 | - name: Get Count 26 | id: vars 27 | run: echo ::set-output name=count::$(node ./.bit/scripts/getProgress.js --repo=${{github.event.repository.name}} --owner=${{github.repository_owner}} ) 28 | 29 | - name: Install Cypress 30 | run: npm install cypress 31 | 32 | - name: Step 51 33 | if: ${{steps.vars.outputs.count == 51 && github.event.head_commit.message != 'Update progress'}} 34 | run: node .bit/tests/spec.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} --spec=cypress/integration/4.1.spec.js 35 | 36 | - name: Step 53 37 | if: ${{steps.vars.outputs.count == 53 && github.event.head_commit.message != 'Update progress'}} 38 | run: node .bit/tests/spec.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} --spec=cypress/integration/4.2.spec.js 39 | 40 | - name: Step 55 41 | if: ${{steps.vars.outputs.count == 55 && github.event.head_commit.message != 'Update progress'}} 42 | run: node .bit/tests/spec.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} --spec=cypress/integration/4.3.spec.js 43 | 44 | - name: Step 57 45 | if: ${{steps.vars.outputs.count == 57 && github.event.head_commit.message != 'Update progress'}} 46 | run: node .bit/tests/spec.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} --spec=cypress/integration/4.4.spec.js 47 | 48 | - name: Check for branch 49 | if: ${{steps.vars.outputs.count > 57 && github.event.head_commit.message != 'Update progress'}} 50 | run: | 51 | node .bit/tests/wrongbranch.js 52 | -------------------------------------------------------------------------------- /.bit/workflows/bunnimage.yml: -------------------------------------------------------------------------------- 1 | name: Bunnimage 2 | on: 3 | push: 4 | branches: 5 | - bunnimage 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v2 14 | 15 | - name: Setup Node Environment 16 | uses: actions/setup-node@v2 17 | with: 18 | node-version: '14' 19 | 20 | - name: Install Dependencies 21 | run: | 22 | npm install minimist 23 | npm install node-fetch 24 | 25 | - name: Get Count 26 | id: vars 27 | run: echo ::set-output name=count::$(node ./.bit/scripts/getProgress.js --repo=${{github.event.repository.name}} --owner=${{github.repository_owner}} ) 28 | 29 | - name: Step 33 30 | if: ${{steps.vars.outputs.count == 33 && github.event.head_commit.message != 'Update progress'}} 31 | env: 32 | storage_account_connection_string: ${{ secrets.storage_account_connection_string }} 33 | container_name: ${{ secrets.container_name }} 34 | run: | 35 | npm install @azure/storage-blob 36 | node .bit/tests/test.3.1.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 37 | - name: Step 35 38 | if: ${{steps.vars.outputs.count == 35 && github.event.head_commit.message != 'Update progress'}} 39 | env: 40 | BUNNIMAGE_ENDPOINT: ${{ secrets.BUNNIMAGE_ENDPOINT }} 41 | container_name: ${{ secrets.container_name }} 42 | blob_url: ${{ secrets.blob_url }} 43 | run: | 44 | npm install node-fetch 45 | npm install form-data 46 | node .bit/tests/test.3.2.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 47 | - name: Step 37 48 | if: ${{steps.vars.outputs.count == 37 && github.event.head_commit.message != 'Update progress'}} 49 | env: 50 | BUNNIMAGE_ENDPOINT: ${{ secrets.BUNNIMAGE_ENDPOINT }} 51 | container_name: ${{ secrets.container_name }} 52 | blob_url: ${{ secrets.blob_url }} 53 | run: | 54 | npm install node-fetch 55 | npm install form-data 56 | node .bit/tests/test.3.3.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 57 | - name: Step 41 58 | if: ${{steps.vars.outputs.count == 41 && github.event.head_commit.message != 'Update progress'}} 59 | env: 60 | BUNNIMAGE_ENDPOINT: ${{ secrets.BUNNIMAGE_ENDPOINT }} 61 | BUNNIMAGE_ENDPOINT2: ${{ secrets.BUNNIMAGE_ENDPOINT2 }} 62 | container_name: ${{ secrets.container_name }} 63 | blob_url: ${{ secrets.blob_url }} 64 | run: | 65 | npm install node-fetch 66 | npm install form-data 67 | node .bit/tests/test.3.5.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 68 | - name: Check for branch 69 | if: ${{steps.vars.outputs.count > 42 && github.event.head_commit.message != 'Update progress'}} 70 | run: | 71 | node .bit/tests/wrongbranch.js 72 | -------------------------------------------------------------------------------- /.bit/workflows/deepsecrets.yml: -------------------------------------------------------------------------------- 1 | name: Deep Secrets 2 | on: 3 | push: 4 | branches: 5 | - deepsecrets 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v2 14 | 15 | - name: Setup Node Environment 16 | uses: actions/setup-node@v2 17 | with: 18 | node-version: '14' 19 | 20 | - name: Install Dependencies 21 | run: | 22 | npm install minimist 23 | npm install node-fetch 24 | 25 | - name: Get Count 26 | id: vars 27 | run: echo ::set-output name=count::$(node ./.bit/scripts/getProgress.js --repo=${{github.event.repository.name}} --owner=${{github.repository_owner}} ) 28 | 29 | - name: Step 43 30 | if: ${{steps.vars.outputs.count == 43 && github.event.head_commit.message != 'Update progress'}} 31 | env: 32 | DEEPSECRETS_ENDPOINT: ${{ secrets.DEEPSECRETS_ENDPOINT }} 33 | run: | 34 | npm install node-fetch 35 | node .bit/tests/test.3.6.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 36 | - name: Step 45 37 | if: ${{steps.vars.outputs.count == 45 && github.event.head_commit.message != 'Update progress'}} 38 | env: 39 | DEEPSECRETS_ENDPOINT: ${{ secrets.DEEPSECRETS_ENDPOINT }} 40 | run: | 41 | npm install node-fetch 42 | node .bit/tests/test.3.7.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 43 | - name: Step 47 44 | if: ${{steps.vars.outputs.count == 47 && github.event.head_commit.message != 'Update progress'}} 45 | env: 46 | DEEPSECRETS_ENDPOINT: ${{ secrets.DEEPSECRETS_ENDPOINT }} 47 | run: | 48 | npm install node-fetch 49 | node .bit/tests/test.3.8.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 50 | - name: Check for branch 51 | if: ${{steps.vars.outputs.count > 47 && github.event.head_commit.message != 'Update progress'}} 52 | run: | 53 | node .bit/tests/wrongbranch.js 54 | -------------------------------------------------------------------------------- /.bit/workflows/emotionalgifs.yml: -------------------------------------------------------------------------------- 1 | name: Emotional Gifs 2 | on: 3 | push: 4 | branches: 5 | - emotionalgifs 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v2 14 | 15 | - name: Setup Node Environment 16 | uses: actions/setup-node@v2 17 | with: 18 | node-version: '14' 19 | 20 | - name: Install Dependencies 21 | run: | 22 | npm install minimist 23 | npm install node-fetch 24 | 25 | - name: Get Count 26 | id: vars 27 | run: echo ::set-output name=count::$(node ./.bit/scripts/getProgress.js --repo=${{github.event.repository.name}} --owner=${{github.repository_owner}} ) 28 | 29 | - name: Step 17 30 | if: ${{steps.vars.outputs.count == 17 && github.event.head_commit.message != 'Update progress'}} 31 | env: 32 | EMOTIONAL_ENDPOINT: ${{ secrets.EMOTIONAL_ENDPOINT }} 33 | run: | 34 | npm install node-fetch 35 | npm install form-data 36 | node .bit/tests/test.2.2.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 37 | 38 | - name: Step 19 39 | if: ${{steps.vars.outputs.count == 19 && github.event.head_commit.message != 'Update progress'}} 40 | env: 41 | EMOTIONAL_ENDPOINT: ${{ secrets.EMOTIONAL_ENDPOINT }} 42 | run: | 43 | npm install node-fetch 44 | npm install form-data 45 | node .bit/tests/test.2.3.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 46 | 47 | - name: Step 21 48 | if: ${{steps.vars.outputs.count == 21 && github.event.head_commit.message != 'Update progress'}} 49 | env: 50 | EMOTIONAL_ENDPOINT: ${{ secrets.EMOTIONAL_ENDPOINT }} 51 | run: | 52 | npm install node-fetch 53 | npm install form-data 54 | node .bit/tests/test.2.4.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 55 | 56 | - name: Step 23 57 | if: ${{steps.vars.outputs.count == 23 && github.event.head_commit.message != 'Update progress'}} 58 | env: 59 | EMOTIONAL_ENDPOINT: ${{ secrets.EMOTIONAL_ENDPOINT }} 60 | run: | 61 | npm install node-fetch 62 | npm install form-data 63 | node .bit/tests/test.2.5.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 64 | 65 | - name: Check for branch 66 | if: ${{steps.vars.outputs.count > 23 && github.event.head_commit.message != 'Update progress'}} 67 | run: | 68 | node .bit/tests/wrongbranch.js 69 | -------------------------------------------------------------------------------- /.bit/workflows/hackervoice.yml: -------------------------------------------------------------------------------- 1 | name: Hacker Voice 2 | on: 3 | push: 4 | branches: 5 | - hackervoice 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v2 14 | 15 | - name: Setup Node Environment 16 | uses: actions/setup-node@v2 17 | with: 18 | node-version: '14' 19 | 20 | - name: Install Dependencies 21 | run: | 22 | npm install minimist 23 | npm install node-fetch 24 | 25 | - name: Get Count 26 | id: vars 27 | run: echo ::set-output name=count::$(node ./.bit/scripts/getProgress.js --repo=${{github.event.repository.name}} --owner=${{github.repository_owner}} ) 28 | 29 | - name: Step 4 30 | if: ${{steps.vars.outputs.count == 4 && github.event.head_commit.message != 'Update progress'}} 31 | env: 32 | HACKERVOICE_ENDPOINT: ${{ secrets.HACKERVOICE_ENDPOINT }} 33 | run: | 34 | npm install node-fetch 35 | node .bit/tests/test.1.4.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 36 | - name: Step 6 37 | if: ${{steps.vars.outputs.count == 6 && github.event.head_commit.message != 'Update progress'}} 38 | env: 39 | HACKERVOICE_ENDPOINT: ${{ secrets.HACKERVOICE_ENDPOINT }} 40 | run: | 41 | npm install node-fetch 42 | node .bit/tests/test.1.5.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 43 | - name: Check for branch 44 | if: ${{steps.vars.outputs.count > 7 && github.event.head_commit.message != 'Update progress'}} 45 | run: | 46 | node .bit/tests/wrongbranch.js 47 | -------------------------------------------------------------------------------- /.bit/workflows/hello.yml: -------------------------------------------------------------------------------- 1 | name: Getting Started 2 | on: 3 | push: 4 | branches: 5 | - hello 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v2 14 | 15 | - name: Setup Node Environment 16 | uses: actions/setup-node@v2 17 | with: 18 | node-version: '14' 19 | 20 | - name: Install Dependencies 21 | run: | 22 | npm install minimist 23 | npm install node-fetch 24 | 25 | - name: Get Count 26 | id: vars 27 | run: echo ::set-output name=count::$(node ./.bit/scripts/getProgress.js --repo=${{github.event.repository.name}} --owner=${{github.repository_owner}} ) 28 | 29 | - name: Step 1 30 | if: ${{steps.vars.outputs.count == 1 && github.event.head_commit.message != 'Update progress'}} 31 | run: | 32 | node .bit/tests/test.1.2.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 33 | - name: Check for branch 34 | if: ${{steps.vars.outputs.count > 2 && github.event.head_commit.message != 'Update progress'}} 35 | run: | 36 | node .bit/tests/wrongbranch.js 37 | -------------------------------------------------------------------------------- /.bit/workflows/morse.yml: -------------------------------------------------------------------------------- 1 | name: Morse 2 | on: 3 | push: 4 | branches: 5 | - morse 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v2 14 | 15 | - name: Setup Node Environment 16 | uses: actions/setup-node@v2 17 | with: 18 | node-version: '14' 19 | 20 | - name: Install Dependencies 21 | run: | 22 | npm install minimist 23 | npm install node-fetch 24 | 25 | - name: Get Count 26 | id: vars 27 | run: echo ::set-output name=count::$(node ./.bit/scripts/getProgress.js --repo=${{github.event.repository.name}} --owner=${{github.repository_owner}} ) 28 | 29 | - name: Step 12 30 | if: ${{steps.vars.outputs.count == 12 && github.event.head_commit.message != 'Update progress'}} 31 | env: 32 | MORSE_ENDPOINT: ${{ secrets.MORSE_ENDPOINT }} 33 | run: | 34 | npm install node-fetch 35 | node .bit/tests/test.1.8.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 36 | - name: Check for branch 37 | if: ${{steps.vars.outputs.count > 12 && github.event.head_commit.message != 'Update progress'}} 38 | run: | 39 | node .bit/tests/wrongbranch.js 40 | -------------------------------------------------------------------------------- /.bit/workflows/song4u.yml: -------------------------------------------------------------------------------- 1 | name: Song 4 U 2 | on: 3 | push: 4 | branches: 5 | - song4u 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v2 14 | 15 | - name: Setup Node Environment 16 | uses: actions/setup-node@v2 17 | with: 18 | node-version: '14' 19 | 20 | - name: Install Dependencies 21 | run: | 22 | npm install minimist 23 | npm install node-fetch 24 | 25 | - name: Get Count 26 | id: vars 27 | run: echo ::set-output name=count::$(node ./.bit/scripts/getProgress.js --repo=${{github.event.repository.name}} --owner=${{github.repository_owner}} ) 28 | 29 | - name: Step 25 30 | if: ${{steps.vars.outputs.count == 25 && github.event.head_commit.message != 'Update progress'}} 31 | env: 32 | SONGREC_ENDPOINT: ${{ secrets.SONGREC_ENDPOINT }} 33 | run: | 34 | npm install node-fetch 35 | node .bit/tests/test.2.6.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 36 | 37 | - name: Step 27 38 | if: ${{steps.vars.outputs.count == 27 && github.event.head_commit.message != 'Update progress'}} 39 | env: 40 | SONGREC_ENDPOINT: ${{ secrets.SONGREC_ENDPOINT }} 41 | run: | 42 | npm install node-fetch 43 | node .bit/tests/test.2.7.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 44 | 45 | - name: Step 29 46 | if: ${{steps.vars.outputs.count == 29 && github.event.head_commit.message != 'Update progress'}} 47 | env: 48 | SONGREC_ENDPOINT: ${{ secrets.SONGREC_ENDPOINT }} 49 | run: | 50 | npm install node-fetch 51 | node .bit/tests/test.2.8.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 52 | - name: Check for branch 53 | if: ${{steps.vars.outputs.count > 29 && github.event.head_commit.message != 'Update progress'}} 54 | run: | 55 | node .bit/tests/wrongbranch.js 56 | -------------------------------------------------------------------------------- /.bit/workflows/twocatz-frontend.yml: -------------------------------------------------------------------------------- 1 | name: twoCatz Frontend 2 | on: 3 | push: 4 | branches: 5 | - twocatz-frontend 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v2 14 | 15 | - name: Setup Node Environment 16 | uses: actions/setup-node@v2 17 | with: 18 | node-version: '14' 19 | 20 | - name: Install Dependencies 21 | run: | 22 | npm install minimist 23 | npm install node-fetch 24 | 25 | - name: Get Count 26 | id: vars 27 | run: echo ::set-output name=count::$(node ./.bit/scripts/getProgress.js --repo=${{github.event.repository.name}} --owner=${{github.repository_owner}} ) 28 | 29 | - name: Install Cypress 30 | run: npm install cypress 31 | 32 | - name: Step 59 33 | if: ${{steps.vars.outputs.count == 59 && github.event.head_commit.message != 'Update progress'}} 34 | run: node .bit/tests/spec.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} --spec=cypress/integration/4.5.spec.js 35 | 36 | - name: Step 61 37 | if: ${{steps.vars.outputs.count == 61 && github.event.head_commit.message != 'Update progress'}} 38 | env: 39 | TWOCATZ_ENDPOINT: ${{ secrets.TWOCATZ_ENDPOINT }} 40 | run: | 41 | npm install node-fetch 42 | node .bit/tests/test.4.6.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 43 | - name: Step 63 44 | if: ${{steps.vars.outputs.count == 63 && github.event.head_commit.message != 'Update progress'}} 45 | run: node .bit/tests/spec.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} --spec=cypress/integration/4.7.spec.js 46 | - name: Check for branch 47 | if: ${{steps.vars.outputs.count > 63 && github.event.head_commit.message != 'Update progress'}} 48 | run: | 49 | node .bit/tests/wrongbranch.js 50 | -------------------------------------------------------------------------------- /.bit/workflows/twocatz.yml: -------------------------------------------------------------------------------- 1 | name: TwoCatz 2 | on: 3 | push: 4 | branches: 5 | - twocatz 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v2 14 | 15 | - name: Setup Node Environment 16 | uses: actions/setup-node@v2 17 | with: 18 | node-version: '14' 19 | 20 | - name: Install Dependencies 21 | run: | 22 | npm install minimist 23 | npm install node-fetch 24 | 25 | - name: Get Count 26 | id: vars 27 | run: echo ::set-output name=count::$(node ./.bit/scripts/getProgress.js --repo=${{github.event.repository.name}} --owner=${{github.repository_owner}} ) 28 | - name: Step 8 29 | if: ${{steps.vars.outputs.count == 8 && github.event.head_commit.message != 'Update progress'}} 30 | env: 31 | TWOCATZ_ENDPOINT: ${{ secrets.TWOCATZ_ENDPOINT }} 32 | run: | 33 | npm install node-fetch 34 | node .bit/tests/test.1.6.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 35 | - name: Step 10 36 | if: ${{steps.vars.outputs.count == 10 && github.event.head_commit.message != 'Update progress'}} 37 | env: 38 | TWOCATZ_ENDPOINT: ${{ secrets.TWOCATZ_ENDPOINT }} 39 | run: | 40 | npm install node-fetch 41 | node .bit/tests/test.1.7.js --repo=${{github.event.repository.name}} --user=${{github.repository_owner}} 42 | - name: Check for branch 43 | if: ${{steps.vars.outputs.count > 10 && github.event.head_commit.message != 'Update progress'}} 44 | run: | 45 | node .bit/tests/wrongbranch.js 46 | -------------------------------------------------------------------------------- /.bit/workflows/week5.yml: -------------------------------------------------------------------------------- 1 | name: Week 5 2 | on: 3 | push: 4 | branches: 5 | - week5 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v2 14 | 15 | - name: Setup Node Environment 16 | uses: actions/setup-node@v2 17 | with: 18 | node-version: '14' 19 | 20 | - name: Install Dependencies 21 | run: | 22 | npm install minimist 23 | npm install node-fetch 24 | 25 | - name: Get Count 26 | id: vars 27 | run: echo ::set-output name=count::$(node ./.bit/scripts/getProgress.js --repo=${{github.event.repository.name}} --owner=${{github.repository_owner}} ) 28 | 29 | -------------------------------------------------------------------------------- /.funcignore: -------------------------------------------------------------------------------- 1 | *.js.map 2 | *.ts 3 | .git* 4 | .vscode 5 | local.settings.json 6 | test 7 | tsconfig.json -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---start-course.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Start Course" 3 | about: Create this issue to start the serverless curriculum! 4 | title: Start Course 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Click the big green button that says `Submit new issue`, and you'll be good to go! 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/content-bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Content Bug Report" 3 | about: Report a bug in our instructions - do we need to add or modify content? 4 | title: "[Content Bug]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 14 | 15 | **Identify Steps:** 16 | * Step _ 17 | * Step _ 18 | 19 | **Week:** 20 | 21 | ## Describe the Issue 22 | 23 | 26 | 27 | ## Proposed Fix 28 | 29 | 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "⭐️ Feature or Content Request" 3 | about: Suggest new features to bot tests or new content to the curriculum. 4 | title: "[Feature]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Use-cases 11 | 14 | 15 | ### Proposal 16 | 19 | 20 | ### References 21 | 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/final-project.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: [FOR STUDENTS] Final Project Issue Template 3 | about: Use this template to create new tasks for your final project 4 | title: "[Task Name]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Description 11 | - [Replace with description] 12 | 13 | ### ETA: 14 | > How long do you think it will take to complete this? 15 | - [Replace with eta] 16 | 17 | ### Objective: 18 | > Checklist of everything you need to do to complete this issue 19 | - [ ] [Replace with small task 1] 20 | - [ ] [Replace with small task 2] 21 | - [ ] [Replace with small task 3] 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/test-bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F9EA Test Code Bug Report" 3 | about: Report a bug in our tests 4 | title: "[Test Bug] Week _ Step _" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 14 | 15 | **Identify Test:** Week ? Step ? 16 | 17 | ## Information 18 | 19 | * [Action Run]() 20 | * [Your Code]() 21 | 22 | ## The current behavior 23 | 24 | 27 | 28 | ## The expected behavior 29 | 30 | 33 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Changes made 2 | * [Replace with summaries of general changes made] 3 | * [Did you modify or delete?] 4 | * [How do your changes fix the issue?] 5 | 6 | Closes #[issue number] -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 14 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 30 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | # Label to use when marking an issue as stale 9 | staleLabel: wontfix 10 | # Comment to post when marking an issue as stale. Set to `false` to disable 11 | markComment: > 12 | This issue has been automatically marked as stale because it has not had 13 | recent activity. It will be closed if no further activity occurs. Thank you 14 | for your contributions. 15 | # Comment to post when 16 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Syncing Your Cabin 2 | on: 3 | push: 4 | branches: 5 | - null 6 | 7 | jobs: 8 | 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout Code 14 | uses: actions/checkout@v2 15 | 16 | - name: Setup Python Environment 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: 3.7 20 | 21 | - name: Getting Filenames 22 | run: | 23 | python .bit/scripts/start.py 24 | - name: Getting Descriptions 25 | run: | 26 | python .bit/scripts/parse.py 27 | - name: Creating Config Files 28 | run: | 29 | python .bit/scripts/create.py 30 | 31 | - name: Setup Git Config 32 | if: ${{github.event.head_commit.message != 'Initial commit'}} 33 | run: | 34 | git config user.name ${{ secrets.USERNAME }} 35 | git config user.email ${{ secrets.EMAIL }} 36 | - name: Commit Synced Changes 37 | if: ${{github.event.head_commit.message != 'Initial commit'}} 38 | run: | 39 | chmod +x .bit/scripts/commit.sh 40 | .bit/scripts/commit.sh 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | 24 | # nyc test coverage 25 | .nyc_output 26 | 27 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 28 | .grunt 29 | 30 | # Bower dependency directory (https://bower.io/) 31 | bower_components 32 | 33 | # node-waf configuration 34 | .lock-wscript 35 | 36 | # Compiled binary addons (https://nodejs.org/api/addons.html) 37 | build/Release 38 | 39 | # Dependency directories 40 | node_modules/ 41 | jspm_packages/ 42 | 43 | # TypeScript v1 declaration files 44 | typings/ 45 | 46 | # Optional npm cache directory 47 | .npm 48 | 49 | # Optional eslint cache 50 | .eslintcache 51 | 52 | # Optional REPL history 53 | .node_repl_history 54 | 55 | # Output of 'npm pack' 56 | *.tgz 57 | 58 | # Yarn Integrity file 59 | .yarn-integrity 60 | 61 | # dotenv environment variables file 62 | .env 63 | .env.test 64 | 65 | # parcel-bundler cache (https://parceljs.org/) 66 | .cache 67 | 68 | # next.js build output 69 | .next 70 | 71 | # nuxt.js build output 72 | .nuxt 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless/ 79 | 80 | # FuseBox cache 81 | .fusebox/ 82 | 83 | # DynamoDB Local files 84 | .dynamodb/ 85 | 86 | # TypeScript output 87 | dist 88 | out 89 | 90 | # Azure Functions artifacts 91 | bin 92 | obj 93 | appsettings.json 94 | local.settings.json 95 | 96 | # local history vscode extension 97 | .history/ 98 | 99 | # DS_Store 100 | .DS_Store 101 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-vscode.vscode-node-azure-pack", 4 | "streetsidesoftware.code-spell-checker", 5 | "davidanson.vscode-markdownlint", 6 | "dbaeumer.vscode-eslint", 7 | "DavidAnson.vscode-markdownlint", 8 | "robertohuertasm.vscode-icons", 9 | "formulahendry.auto-rename-tag", 10 | "eamodio.gitlens", 11 | "live server", 12 | "material icon theme", 13 | "hookyqr.beautify", 14 | "esbenp.prettier-vscode", 15 | "christian-kohler.path-intellisense" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /blog.md: -------------------------------------------------------------------------------- 1 | # [Replace this with your project title] 2 | 3 | ## About Me 4 | 5 | ## The Premise 6 | 7 | ## Tools used 8 | 9 | ## Step by step (with code snippets) 10 | 11 | ## Challenges + lessons learned 12 | 13 | ## Thanks and Acknowledgements 14 | -------------------------------------------------------------------------------- /bunnimage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | The best form 7 | 8 | 9 |
10 |

Give your code some love ❤️

11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 |
19 | -------------------------------------------------------------------------------- /checkup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | nodev=$(node -v); 4 | funcv=$(func -v); 5 | 6 | echo "🛑 Note: If you've installed node and Azure Functions Core Tools but this script is still saying you haven't, please restart your terminal and run the script again." 7 | echo "\n" 8 | echo "--------------------------------"; 9 | echo "Checking if node is installed..."; 10 | echo "--------------------------------"; 11 | if [[ $nodev ]] 12 | then 13 | if [[ ${nodev:1:2} -lt 14 ]] 14 | then 15 | echo "❗️ Your node version is $nodev, which is out of maintenance."; 16 | echo "Please upgrade your node version to at least version 14.x"; 17 | echo "https://nodejs.org/en/download/"; 18 | echo "Run this script again after to continue."; 19 | exit 20 | else 21 | echo "✅ Node $nodev is installed, skipping..."; 22 | fi 23 | else 24 | echo "❗️ Node is not installed."; 25 | echo "Please install node here: https://nodejs.org/en/download/"; 26 | echo "Run this script again after to continue."; 27 | exit 28 | fi 29 | 30 | echo "\n" 31 | echo "------------------------------------------------------"; 32 | echo "Checking if Azure Functions Core Tools is installed..."; 33 | echo "------------------------------------------------------"; 34 | if [[ $funcv ]] 35 | then 36 | if [[ ${funcv:0:1} < 3 ]] 37 | then 38 | echo "❗️ Please upgrade your Azure Functions Core Tools version to version 4."; 39 | echo "Follow this link to learn how to: https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=v4%2Clinux%2Ccsharp%2Cportal%2Cbash#changing-core-tools-versions"; 40 | echo "Run this script again after to continue."; 41 | exit 42 | fi 43 | echo "✅ Azure Function Core Tools v$funcv is installed, skipping..."; 44 | else 45 | echo "❌ Azure Function Core Tools is not installed."; 46 | echo "Please install it here: https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=v4%2Clinux%2Ccsharp%2Cportal%2Cbash#install-the-azure-functions-core-tools"; 47 | echo "Run this script again after to continue."; 48 | exit 49 | fi 50 | 51 | echo "\n" 52 | echo "---------------------------------------" 53 | echo "Checking if versions are compatible..." 54 | echo "---------------------------------------" 55 | if [[ (${nodev:1:2} == "16" || ${nodev:1:2} == "14") && (${funcv:0:1} == "4") ]] 56 | then 57 | echo "✅ Compatible versions! You are good to go."; 58 | elif [[ (${nodev:1:2} == "14") && (${funcv:0:1} == "3") ]] 59 | then 60 | echo "✅ Compatible versions! You are good to go."; 61 | elif [[ ${funcv:0:1} == "3" ]] 62 | then 63 | echo "❗️ Your node version is $nodev but your Azure Core Tools version is $funcv"; 64 | echo "Please upgrade your Azure Core Tools version to version 4.x"; 65 | echo "Follow this link to learn how to: https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=v4%2Clinux%2Ccsharp%2Cportal%2Cbash#changing-core-tools-versions"; 66 | echo "Run this script again after to continue."; 67 | fi -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "fixturesFolder": "cypress/fixtures", 3 | "supportFile": false, 4 | "pluginsFile": false 5 | } -------------------------------------------------------------------------------- /cypress/fixtures/testimage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitprj/Intro-To-Serverless/7fd8024b8d259eb4772785db540bc0e01f34c3c1/cypress/fixtures/testimage.jpg -------------------------------------------------------------------------------- /cypress/integration/4.1.spec.js: -------------------------------------------------------------------------------- 1 | describe('Testing Bunnimage', () => { 2 | it('Testing Week 4 Step 1', () => { 3 | cy.visit('bunnimage/index.html') 4 | cy.get('input[type="text"]').type('console.log("hi yall")') 5 | cy.get('input[type="submit"]').click() 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /cypress/integration/4.2.spec.js: -------------------------------------------------------------------------------- 1 | const uploadFile = (fileName, fileType = '', selector) => { 2 | cy.get(selector).then(subject => { 3 | cy.fixture(fileName, 'base64') 4 | .then(Cypress.Blob.base64StringToBlob) 5 | .then(blob => { 6 | const el = subject[0] 7 | const testFile = new File([blob], fileName, { type: fileType }) 8 | const dataTransfer = new DataTransfer() 9 | dataTransfer.items.add(testFile) 10 | el.files = dataTransfer.files 11 | console.log(el.files) 12 | }) 13 | }) 14 | } 15 | 16 | describe('Test Bunnimage', () => { 17 | it('Testing form submission', () => { 18 | cy.visit('bunnimage/index.html') 19 | const fileName = "testimage.jpg" 20 | cy.get('input[type="text"]').type('acode') 21 | uploadFile(fileName, 'image/jpg', 'input[name="image"]') 22 | cy.get('input[type="submit"]').click() 23 | cy.get('#output').contains('Thanks!') 24 | }) 25 | 26 | it('Testing error catching', () => { 27 | cy.visit('bunnimage/index.html') 28 | const fileName = "testimage.jpg" 29 | uploadFile(fileName, 'image/jpg', 'input[name="image"]') 30 | cy.get('input[type="submit"]').click() 31 | cy.on('window:alert', (str) => { 32 | expect(str).to.equal(`No name error.`) 33 | }) 34 | }) 35 | }) -------------------------------------------------------------------------------- /cypress/integration/4.3.spec.js: -------------------------------------------------------------------------------- 1 | const uploadFile = (fileName, fileType = '', selector) => { 2 | cy.get(selector).then(subject => { 3 | cy.fixture(fileName, 'base64') 4 | .then(Cypress.Blob.base64StringToBlob) 5 | .then(blob => { 6 | const el = subject[0] 7 | const testFile = new File([blob], fileName, { type: fileType }) 8 | const dataTransfer = new DataTransfer() 9 | dataTransfer.items.add(testFile) 10 | el.files = dataTransfer.files 11 | console.log(el.files) 12 | }) 13 | }) 14 | } 15 | 16 | describe('Test Bunnimage', () => { 17 | it('Testing form submission', () => { 18 | cy.visit('bunnimage/index.html') 19 | const fileName = "testimage.jpg" 20 | cy.get('input[type="text"]').type('mysecret') 21 | uploadFile(fileName, 'image/jpeg', 'input[name="image"]') 22 | cy.get('input[type="submit"]').click() 23 | cy.wait(5000) 24 | cy.get('#output').contains('Your image has been stored successfully!') 25 | }) 26 | }) -------------------------------------------------------------------------------- /cypress/integration/4.4.spec.js: -------------------------------------------------------------------------------- 1 | const uploadFile = (fileName, fileType = '', selector) => { 2 | cy.get(selector).then(subject => { 3 | cy.fixture(fileName, 'base64') 4 | .then(Cypress.Blob.base64StringToBlob) 5 | .then(blob => { 6 | const el = subject[0] 7 | const testFile = new File([blob], fileName, { type: fileType }) 8 | const dataTransfer = new DataTransfer() 9 | dataTransfer.items.add(testFile) 10 | el.files = dataTransfer.files 11 | console.log(el.files) 12 | }) 13 | }) 14 | } 15 | 16 | describe('Test Bunnimage', () => { 17 | it('Testing upload', () => { 18 | cy.visit('bunnimage/index.html') 19 | const fileName = "testimage.jpg" 20 | cy.get('input[id="username"]').type('acode') 21 | uploadFile(fileName, 'image/jpg', 'input[name="image"]') 22 | cy.get('input[id="button1"]').click() 23 | cy.get('#output').contains('Your image has been stored successfully!') 24 | }) 25 | 26 | it('Testing download', () => { 27 | cy.get('input[id="downloadusername"]').type('acode') 28 | cy.get('input[id="button2"]').click() 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /cypress/integration/4.5.spec.js: -------------------------------------------------------------------------------- 1 | describe('Testing the CatzAPP', () => { 2 | it('Testing Week 4 Step 5', () => { 3 | cy.visit('catzapp/index.html') 4 | cy.get('input[type="text"]').type('cat') 5 | cy.get('input[type="button"]').click() 6 | cy.get('img').should('have.attr', 'src').should('include','https://cataas.com/cat/says/cat') 7 | }) 8 | }) -------------------------------------------------------------------------------- /cypress/integration/4.7.spec.js: -------------------------------------------------------------------------------- 1 | describe('Testing the CatzAPP', () => { 2 | it('Testing Week 4 Step 7', () => { 3 | cy.visit('catzapp/index.html') 4 | cy.get('input[id="name1"]').type('cat1') 5 | cy.get('input[id="name2"]').type('cat2') 6 | cy.get('input[id="name3"]').type('cat3') 7 | cy.get('input[id="name4"]').type('cat4') 8 | cy.get('input[type="button"]').click() 9 | cy.wait(25000) 10 | cy.get('img[id="image1"]').should('have.attr', 'src').should('include','data:image/png;base64,') 11 | cy.get('img[id="image2"]').should('have.attr', 'src').should('include','data:image/png;base64,') 12 | cy.get('img[id="image3"]').should('have.attr', 'src').should('include','data:image/png;base64,') 13 | cy.get('img[id="image4"]').should('have.attr', 'src').should('include','data:image/png;base64,') 14 | }) 15 | }) -------------------------------------------------------------------------------- /cypress/integration/cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "fixturesFolder": "cypress/fixtures", 3 | "supportFile": false, 4 | "pluginsFile": false 5 | } -------------------------------------------------------------------------------- /host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "applicationInsights": { 5 | "samplingSettings": { 6 | "isEnabled": true, 7 | "excludedTypes": "Request" 8 | } 9 | } 10 | }, 11 | "extensionBundle": { 12 | "id": "Microsoft.Azure.Functions.ExtensionBundle", 13 | "version": "[2.*, 3.0.0)" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "intro-to-serverless", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "start": "func start", 7 | "test": "echo \"No tests yet...\"" 8 | }, 9 | "dependencies": { 10 | "cypress": "^8.2.0", 11 | "node-fetch": "^2.6.1" 12 | }, 13 | "devDependencies": {} 14 | } 15 | -------------------------------------------------------------------------------- /project/tech.md: -------------------------------------------------------------------------------- 1 | # Technologies 2 | 3 | ### Azure Services 4 | 5 | **Name of Service** 6 | - [replace with bullet list of what purpose(s) it serves for my project] 7 | - [include how it will interact with other components of your project listed above/below] 8 | 9 | ### APIs 10 | 11 | **Name of API** 12 | - [replace with bullet list of what purpose(s) it serves for my project] 13 | - [include how it will interact with other components of your project listed above/below] 14 | 15 | ### Packages/Libraries/Databases 16 | 17 | **Name of Packages/Library/Database** 18 | - [replace with bullet list of what purpose(s) it serves for my project] 19 | - [include how it will interact with other components of your project listed above/below] 20 | 21 | ### Front-end Languages 22 | 23 | **Name of Language** 24 | - [replace with bullet list of what purpose(s) it serves for my project] 25 | - [include how it will interact with other components of your project listed above/below] 26 | 27 | ### Flowchart 28 | 29 | [Replace with image of final flowchart] -------------------------------------------------------------------------------- /project/timeline.md: -------------------------------------------------------------------------------- 1 | # Timeline 2 | > Note: Copy and paste the task template (bottom of page) 3 | 4 | ## Week 1 5 | 6 | ### [Task Name]: 7 | 8 | #### Description 9 | - [Replace with description] 10 | 11 | #### ETA: 12 | > How long do you think it will take to complete this? 13 | - [Replace with eta] 14 | 15 | #### Objective: 16 | > Checklist of everything you need to do to complete this issue 17 | - [ ] [Replace with small task 1] 18 | - [ ] [Replace with small task 2] 19 | - [ ] [Replace with small task 3] 20 | 21 | ## Week 2 22 | 23 | ## Week 3 24 | 25 | ## Week 4 26 | 27 | 28 | --- 29 | 30 |
Task Template 31 |
32 | 33 | ### [Task Name]: 34 | 35 | #### Description 36 | - [Replace with description] 37 | 38 | #### ETA: 39 | > How long do you think it will take to complete this? 40 | - [Replace with eta] 41 | 42 | #### Objective: 43 | > Checklist of everything you need to do to complete this issue 44 | - [ ] [Replace with small task 1] 45 | - [ ] [Replace with small task 2] 46 | - [ ] [Replace with small task 3] 47 | 48 |

49 |
-------------------------------------------------------------------------------- /proxies.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/proxies", 3 | "proxies": {} 4 | } 5 | --------------------------------------------------------------------------------