From 73d6b70f6aebf0d965934b8839bd3f88bbbe58e8 Mon Sep 17 00:00:00 2001 From: robvk Date: Sun, 7 Aug 2022 12:27:03 +0200 Subject: [PATCH 01/31] Update MAKEME.md Fixes #575 --- week1/MAKEME.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/week1/MAKEME.md b/week1/MAKEME.md index e1d35d0ff..101c247c8 100644 --- a/week1/MAKEME.md +++ b/week1/MAKEME.md @@ -61,7 +61,7 @@ Each week you'll be building a certain part of it. This week we'll get started w After writing all this code you can verify that it's working by running `node server.js` from the Command Line and checking your browser at `http://localhost:3000`. The page should display the message `hello from backend to frontend!`. -### 4.1 Adding a POST request +### 5.1 Adding a POST request In this part we'll add another endpoint, with a `POST` method. From 5e01f52620cddb784a4162ccad519af1be78e650 Mon Sep 17 00:00:00 2001 From: robvk Date: Sun, 7 Aug 2022 12:36:42 +0200 Subject: [PATCH 02/31] Update README.md Fixes #576 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c1b745e46..f23c312c8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ > If you are following the HackYourFuture curriculum we recommend you to start with module 1: [HTML/CSS/GIT](https://github.com/HackYourFuture/HTML-CSS). To get a complete overview of the HackYourFuture curriculum first, click [here](https://github.com/HackYourFuture/curriculum). -> Please help us improve and share your feedback! If you find better tutorials or links, please share them by [opening a pull request](https://github.com/HackYourFuture/JavaScript1/pulls). +> Please help us improve and share your feedback! If you find better tutorials or links, please share them by [opening a pull request](https://github.com/HackYourFuture/Node.js/pulls). # Module #5 - Understand backend: creating web servers with JavaScript using Node.js (Backend) @@ -51,9 +51,9 @@ This repository consists of 3 essential parts: Let's say you are just starting out with the Node.js module. This is what you do... -1. The week always starts on **Wednesday**. First thing you'll do is open the `README.md` for that week. For the first week of `Node.js`, that would be [Week1 Reading](/Week1/README.md) +1. The week always starts on **Wednesday**. First thing you'll do is open the `README.md` for that week. For the first week of `Node.js`, that would be [Week1 Reading](./week1/README.md) 2. You spend **Wednesday** and **Thursday** going over the resources and try to get a basic understanding of the concepts. In the meanwhile, you'll also implement any feedback you got on last week's homework (from the JavaScript3 module) -3. On **Friday** you start with the homework, found in the `MAKEME.md`. For the first week of `Node.js`, that would be [Week1 Homework](/Week1/MAKEME.md) +3. On **Friday** you start with the homework, found in the `MAKEME.md`. For the first week of `Node.js`, that would be [Week1 Homework](/week1/MAKEME.md) 4. You spend **Friday** and **Saturday** playing around with the exercises and write down any questions you might have 5. **DEADLINE 1**: You'll submit any questions you might have before **Saturday 23.59**, in the class channel 6. On **Sunday** you'll attend class. It'll be of the Q&A format, meaning that there will be no new material. Instead your questions shall be discussed and you can learn from others From 2f2401c71fffabc70b846d71294535e9bf77be2e Mon Sep 17 00:00:00 2001 From: robvk Date: Wed, 9 Nov 2022 16:42:17 +0100 Subject: [PATCH 03/31] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f23c312c8..ad6b88c65 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ This repository consists of 3 essential parts: Let's say you are just starting out with the Node.js module. This is what you do... 1. The week always starts on **Wednesday**. First thing you'll do is open the `README.md` for that week. For the first week of `Node.js`, that would be [Week1 Reading](./week1/README.md) -2. You spend **Wednesday** and **Thursday** going over the resources and try to get a basic understanding of the concepts. In the meanwhile, you'll also implement any feedback you got on last week's homework (from the JavaScript3 module) +2. You spend **Wednesday** and **Thursday** going over the resources and try to get a basic understanding of the concepts. In the meanwhile, you'll also implement any feedback you got on last week's homework (from the Using API's module) 3. On **Friday** you start with the homework, found in the `MAKEME.md`. For the first week of `Node.js`, that would be [Week1 Homework](/week1/MAKEME.md) 4. You spend **Friday** and **Saturday** playing around with the exercises and write down any questions you might have 5. **DEADLINE 1**: You'll submit any questions you might have before **Saturday 23.59**, in the class channel From 3d0e06620e45fb6350b882f48253cf205df8d170 Mon Sep 17 00:00:00 2001 From: robvk Date: Mon, 28 Nov 2022 16:20:14 +0100 Subject: [PATCH 04/31] Update README.md --- week2/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/week2/README.md b/week2/README.md index 996786b6c..de94d5d50 100644 --- a/week2/README.md +++ b/week2/README.md @@ -16,7 +16,7 @@ ## 0. Video Lectures -Your teacher Andrej has made video lectures for this week's material that complements the reading material. You can find them here: [Videos 7 - 11](https://www.youtube.com/playlist?list=PLVYDhqbgYpYXpc_l_Vlj8yz3LjgkkWXnn) (up to and including Middleware) +Your teacher Andrej has made video lectures for this week's material that complements the reading material. You can find them here: [Videos 7 - 10](https://www.youtube.com/playlist?list=PLVYDhqbgYpYXpc_l_Vlj8yz3LjgkkWXnn) (up to and including Middleware) HYF Video From beaf43be26eff749864cf0d6fe4230ecbf4d16ff Mon Sep 17 00:00:00 2001 From: robvk Date: Mon, 28 Nov 2022 16:20:23 +0100 Subject: [PATCH 05/31] Update README.md --- week2/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/week2/README.md b/week2/README.md index de94d5d50..da0b6f04e 100644 --- a/week2/README.md +++ b/week2/README.md @@ -16,7 +16,7 @@ ## 0. Video Lectures -Your teacher Andrej has made video lectures for this week's material that complements the reading material. You can find them here: [Videos 7 - 10](https://www.youtube.com/playlist?list=PLVYDhqbgYpYXpc_l_Vlj8yz3LjgkkWXnn) (up to and including Middleware) +Your teacher Andrej has made video lectures for this week's material that complements the reading material. You can find them here: [Videos 7 - 10](https://www.youtube.com/playlist?list=PLVYDhqbgYpYXpc_l_Vlj8yz3LjgkkWXnn) HYF Video From fff99b7731509f3cd45c435577130ce268476272 Mon Sep 17 00:00:00 2001 From: robvk Date: Tue, 17 Jan 2023 09:47:40 +0100 Subject: [PATCH 06/31] Update MAKEME.md No UI anymore, so nothing to show for the project --- week1/MAKEME.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/week1/MAKEME.md b/week1/MAKEME.md index 101c247c8..3ee560ac8 100644 --- a/week1/MAKEME.md +++ b/week1/MAKEME.md @@ -48,7 +48,7 @@ Inside of your `Node.js` fork, go to the folder `week1`. Inside of that folder, > In this part of the homework you'll be setting up the basis of your project: `HackYourTemperature`. Inside the folder `homework`, create a new folder called `hackyourtemperature`. You'll add to it every week. -In this module you'll be rebuilding an existing application, starting from scratch. The application is called `HackYourTemperature` and you can find it here: [HackYourTemperature](https://hackyourtemperature.herokuapp.com/). +In this module you'll be building the simplest of API's, starting from scratch. Each week you'll be building a certain part of it. This week we'll get started with creating a web server, using [Express.js](https://expressjs.com/). Inside of the `hackyourtemperature` folder: From fc788fee475c55bc1eb8bc8927f5003596e1b58a Mon Sep 17 00:00:00 2001 From: robvk Date: Mon, 30 Jan 2023 11:20:12 +0100 Subject: [PATCH 07/31] Update README.md Update link to working API --- week2/practice-exercises/1-joke-api/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/week2/practice-exercises/1-joke-api/README.md b/week2/practice-exercises/1-joke-api/README.md index 669b757dd..96a54f3b8 100644 --- a/week2/practice-exercises/1-joke-api/README.md +++ b/week2/practice-exercises/1-joke-api/README.md @@ -2,7 +2,7 @@ Did you know that there is an API for Chuck Norris jokes? That's incredible, right!? -Write a small JavaScript function that calls this API [The Internet Chuck Norris Databases](http://www.icndb.com/api/) and returns a random joke. You'll be using the `node` command to execute the JavaScript file. To `GET` a random joke inside the function, use the API: http://www.icndb.com/api/ (see `node-fetch`). Make use of `async/await` and `try/catch`. +Write a small JavaScript function that calls this API [The Internet Chuck Norris Databases](http://www.icndb.com/api/) and returns a random joke. You'll be using the `node` command to execute the JavaScript file. To `GET` a random joke inside the function, use the API: https://api.chucknorris.io/jokes/random (see `node-fetch`). Make use of `async/await` and `try/catch`. Hints: From 425a7126435950f94bf4acb035909c0a66b1a24e Mon Sep 17 00:00:00 2001 From: robvk Date: Mon, 5 Jun 2023 15:40:14 +0200 Subject: [PATCH 08/31] Update title image --- assets/nodejs.png | Bin 101310 -> 37739 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/nodejs.png b/assets/nodejs.png index 33f1445e08e5fc2a88715baa0a194bfad6cfddfc..3501f481b1d85641427f3e37fbdbb593aa9cc513 100644 GIT binary patch literal 37739 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 literal 101310 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ï!A&3Y4MGV8&QaRor40Z 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 z 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 IW)k>+0IcWGtN;K2 From 8411b05226a08ba165cf22ffb57d3991d9c23362 Mon Sep 17 00:00:00 2001 From: robvk Date: Thu, 13 Jul 2023 17:21:32 +0200 Subject: [PATCH 09/31] Update planning for career training 1 and 2 --- week1/README.md | 11 +++++++++++ week2/MAKEME.md | 18 +++++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/week1/README.md b/week1/README.md index 625a8646e..43a94f301 100644 --- a/week1/README.md +++ b/week1/README.md @@ -35,6 +35,17 @@ In the Node.js page you have read about different modules (or packages as some c Lastly, testing your API's without a frontend is a little cumbersome. Have a look at the tool Postman that is used a lot to test out API's [here](https://study.hackyourfuture.net/#/tools/postman.md) +## Career training 2 (Interview preparation) + +It is time to start practicing interviews as it is a big part of the hiring process. The [interview preparation repository](https://github.com/HackYourFuture/interviewpreparation) goes over the different types of interviews you will have at companies as well as interviews that you will have upcoming during the curriculum. + +### Career training 2: Planning + +You don't have to do all of this at this moment. This week you will get a message in your class channel about when the Career Training 2 session will be. _Before_ that session make sure to have: + +- Read the whole [‘Interview Preparation’ Repo](https://github.com/HackYourFuture/interviewpreparation). +- Done the homework: make a copy of [this file](https://docs.google.com/document/u/2/d/114rTGS4eG6tpkrMAyVIdvgTrnpmkRL6ax_smkw1B0HI/copy) and submit your answers to the team [here](https://hackyourfuture.typeform.com/to/s6zYAugm). + ## Extra reading If you have time left over this week (or any week this module) then it is a good idea to look at the topic of 'the internet' and learn how it works [here](https://study.hackyourfuture.net/#/the-internet/). This will be valuable when you get into the more complex applications and may also help your understanding of the big picture. diff --git a/week2/MAKEME.md b/week2/MAKEME.md index cd79b22ad..f340f50b8 100644 --- a/week2/MAKEME.md +++ b/week2/MAKEME.md @@ -6,7 +6,8 @@ 2. Practice exercises 3. PROJECT: HackYourTemperature II 4. Code alongs -5. Optional: Side project ideas +5. Career Training 2 (If not completed yet) +6. Optional: Side project ideas ## **1. Prep exercises** @@ -137,23 +138,30 @@ Enjoy! - [Ebook Sales Application](https://www.youtube.com/watch?v=QT3_zT97_1g) -## **5. Optional: Side project ideas** +## **5. Career Training 2 (If not completed yet)** + +Remember that the Career Training 2 session is coming up (check your class channel on slack for the exact date). Before the session make sure you have: + +- Read the whole [‘Interview Preparation’ Repo](https://github.com/HackYourFuture/interviewpreparation). +- Done the homework: make a copy of [this file](https://docs.google.com/document/u/2/d/114rTGS4eG6tpkrMAyVIdvgTrnpmkRL6ax_smkw1B0HI/copy) and submit your answers to the team [here](https://hackyourfuture.typeform.com/to/s6zYAugm). + +## **6. Optional: Side project ideas** > A part of the HackYourFuture curriculum is to work on as many side projects as you can throughout the time you have. This is a nice way to add extra knowledge to your arsenal and show in your CV that you are motivated to learn new technologies. There are plenty of people available to help you out in the `#get-help` channel on Slack so definitely make use of that! Have a look at the [hyf_projects repo](https://github.com/HackYourFuture/hyf_projects/blob/main/README.md#project-2-a-try-out-application) for more details. -### 5.1 Document your API! +### 6.1 Document your API! When using API's in the `Using API's` module you will have noticed that those API's all have extensive documentation on how to use it. As developers like to build tools for everything there are quite a few good tools to semi-automatically document your API from your code! Saves a lot of work and makes sure that you don't forget to update the documentation if the code changes! Add automatic documentation to your API by using one of these tools (Swagger, apiDoc or docbox)! -### 5.2 Web Sockets +### 6.2 Web Sockets It is becoming normal that all webpages automatically refresh whenever there is new data available. Think about the live news feeds that tell you when there is a new item, or that there is a new message on twitter. This is all implemented using Web Sockets, where you as a programmer can set up a link between your page and the server. Have a go by building a simple full stack chat application with an express websocket server! -### 5.3 GraphQL +### 6.3 GraphQL We focused solely on the REST way of building an API, but there is a different way called `GraphQL`. This allows the frontend to define in their query the data that they want to get back. Very cool, but also quite complex. If you are up for a challenge, try to recreate your project using GraphQL (`express-graphql` package is probably the easiest way)! From faddc9745ea5379bb34795c9f60a4ed28ff52022 Mon Sep 17 00:00:00 2001 From: robvk Date: Mon, 14 Aug 2023 15:03:05 +0200 Subject: [PATCH 10/31] Update README.md Update broken link --- week2/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/week2/README.md b/week2/README.md index da0b6f04e..5fd499af6 100644 --- a/week2/README.md +++ b/week2/README.md @@ -11,7 +11,7 @@ - How to consume an external API? - Example of middleware 4. [Automated API testing](https://study.hackyourfuture.net/#/testing/api-testing.md) - - [Postman](https://www.postman.com/use-cases/api-testing-automation/) + - [Postman](https://www.postman.com/automated-testing/) - [supertest](https://www.npmjs.com/package/supertest) ## 0. Video Lectures From 13ab434a3f46b9b86d1b102b08082b35939183cb Mon Sep 17 00:00:00 2001 From: robvk Date: Wed, 13 Sep 2023 14:04:26 +0200 Subject: [PATCH 11/31] Rename homework to assignments --- .gitignore | 2 +- .vscode/launch.json | 12 +++---- README.md | 20 ++++++------ ...k-guide.md => hand-in-assignments-guide.md | 23 +++++++------ week1/LESSONPLAN.md | 32 +++++++++++-------- week1/MAKEME.md | 8 ++--- week1/README.md | 2 +- week2/MAKEME.md | 10 +++--- 8 files changed, 57 insertions(+), 52 deletions(-) rename hand-in-homework-guide.md => hand-in-assignments-guide.md (52%) diff --git a/.gitignore b/.gitignore index f4cecba1d..b0c0b114e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .DS_Store bin -homework-solution +assignments-solution node_modules npm-debug.log package-lock.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 8071fa0cb..1348a90a2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,20 +7,20 @@ { "type": "node", "request": "launch", - "name": "Launch Week 1 - Homework", - "program": "${workspaceFolder}/week1/homework/src/index.js", - "cwd": "${workspaceFolder}/week1/homework" + "name": "Launch Week 1 - Assignments", + "program": "${workspaceFolder}/week1/assignments/src/index.js", + "cwd": "${workspaceFolder}/week1/assignments" }, { "type": "node", "request": "launch", - "name": "Launch Week 1 - Homework Tests", - "program": "${workspaceRoot}/week1/homework/node_modules/ava/profile.js", + "name": "Launch Week 1 - Assignments Tests", + "program": "${workspaceRoot}/week1/assignments/node_modules/ava/profile.js", "args": [ "--concurrency=1", "--no-color", "--serial", - "${workspaceRoot}/week1/homework/test/server.test.js" + "${workspaceRoot}/week1/assignments/test/server.test.js" ], "skipFiles": [ "/**/*.js" diff --git a/README.md b/README.md index ad6b88c65..93eee5aba 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,8 @@ Verify the installation by running `node -v` (-v is short for version) from the This repository consists of 3 essential parts: -1. `README`: this document contains all the required theory you need to understand **while** working on the homework. It contains not only the right resources to learn about the concepts, but also lectures done by HackYourFuture teachers. This is the **first thing** you should start with every week -2. `MAKEME`: this document contains the instructions for each week's homework. Start with the exercises rather quickly, so that you can ground the concepts you read about earlier. +1. `README`: this document contains all the required theory you need to understand **while** working on the assignments. It contains not only the right resources to learn about the concepts, but also lectures done by HackYourFuture teachers. This is the **first thing** you should start with every week +2. `MAKEME`: this document contains the instructions for each week's assignments. Start with the exercises rather quickly, so that you can ground the concepts you read about earlier. 3. `LESSONPLAN`: this document is meant for teachers as a reference. However, as a student don't be shy to take a look at it as well! ### How to study @@ -52,13 +52,13 @@ This repository consists of 3 essential parts: Let's say you are just starting out with the Node.js module. This is what you do... 1. The week always starts on **Wednesday**. First thing you'll do is open the `README.md` for that week. For the first week of `Node.js`, that would be [Week1 Reading](./week1/README.md) -2. You spend **Wednesday** and **Thursday** going over the resources and try to get a basic understanding of the concepts. In the meanwhile, you'll also implement any feedback you got on last week's homework (from the Using API's module) -3. On **Friday** you start with the homework, found in the `MAKEME.md`. For the first week of `Node.js`, that would be [Week1 Homework](/week1/MAKEME.md) +2. You spend **Wednesday** and **Thursday** going over the resources and try to get a basic understanding of the concepts. In the meanwhile, you'll also implement any feedback you got on last week's assignments (from the Using API's module) +3. On **Friday** you start with the assignments, found in the `MAKEME.md`. For the first week of `Node.js`, that would be [Week1 Assignments](/week1/MAKEME.md) 4. You spend **Friday** and **Saturday** playing around with the exercises and write down any questions you might have 5. **DEADLINE 1**: You'll submit any questions you might have before **Saturday 23.59**, in the class channel 6. On **Sunday** you'll attend class. It'll be of the Q&A format, meaning that there will be no new material. Instead your questions shall be discussed and you can learn from others -7. You spend **Monday** and **Tuesday** finalizing your homework -8. **DEADLINE 2**: You submit your homework to the right channels (GitHub) before **Tuesday 23.59**. If you can't make it on time, please communicate it with your mentor +7. You spend **Monday** and **Tuesday** finalizing your assignments +8. **DEADLINE 2**: You submit your assignments to the right channels (GitHub) before **Tuesday 23.59**. If you can't make it on time, please communicate it with your mentor 9. Start the new week by going back to point 1! In summary: @@ -83,10 +83,10 @@ Learn from Andrej in the following playlist of videos he has made for you! (Clic ## Planning -| Week | Topic | Readings | Homework | Lesson Plan | -| ---: | ----------------------------------- | ------------------------------ | ------------------------------ | ------------------------------------- | -| 1. | Client-server model, HTTP & Express | [Readings W1](week1/README.md) | [Homework W1](week1/MAKEME.md) | [Lesson Plan W1](week1/LESSONPLAN.md) | -| 2. | REST, CRUD, API calls, Testing | [Readings W2](week2/README.md) | [Homework W2](week2/MAKEME.md) | [Lesson Plan W2](week2/LESSONPLAN.md) | +| Week | Topic | Readings | Assignments | Lesson Plan | +| ---: | ----------------------------------- | ------------------------------ | --------------------------------- | ------------------------------------- | +| 1. | Client-server model, HTTP & Express | [Readings W1](week1/README.md) | [Assignments W1](week1/MAKEME.md) | [Lesson Plan W1](week1/LESSONPLAN.md) | +| 2. | REST, CRUD, API calls, Testing | [Readings W2](week2/README.md) | [Assignments W2](week2/MAKEME.md) | [Lesson Plan W2](week2/LESSONPLAN.md) | ## Finished? diff --git a/hand-in-homework-guide.md b/hand-in-assignments-guide.md similarity index 52% rename from hand-in-homework-guide.md rename to hand-in-assignments-guide.md index f9dce4f07..8fbd06002 100644 --- a/hand-in-homework-guide.md +++ b/hand-in-assignments-guide.md @@ -1,18 +1,18 @@ -# How to hand in homework +# How to hand in assignments -In this module you'll submit your homework only using GIT and GitHub. +In this module you'll submit your assignments only using GIT and GitHub. 1. [GitHub](https://www.github.com/HackYourFuture/Node.js) -## 1. GitHub homework guide +## 1. GitHub assignments guide -HYF Video +HYF Video -Watch the video (by clicking the image) or go through the following walk-through to learn how to submit your homework: +Watch the video (by clicking the image) or go through the following walk-through to learn how to submit your assignments: ONE TIME ONLY (START OF EVERY MODULE) -1. Create a [fork](https://help.github.com/en/articles/fork-a-repo) of the homework module repository. For Node.js, the homework module repository is `https://www.github.com/HackYourHomework/Node.js-classXX` where XX is your class number. You do this by using the `fork` option on the top right +1. Create a [fork](https://help.github.com/en/articles/fork-a-repo) of the assignments module repository. For Node.js, the assignments module repository is `https://www.github.com/HackYourAssignment/Node.js-classXX` where XX is your class number. You do this by using the `fork` option on the top right 2. Navigate to the URL of the cloned repository (it should be in your personal GitHub account, under "repositories") 3. Clone the repository, using SSH, to your local machine. You can do this by typing in `git clone ` in the command line 4. On your local machine, navigate to the folder using the command line @@ -21,19 +21,18 @@ ONE TIME ONLY (START OF EVERY MODULE) EVERY WEEK 1. Do a `git pull` on your main branch to get the latest version. -2. Create a new branch for each week you have homework. For example, for the week 1 homework for Node create a branch called `YOUR_NAME-w1-Node`. Don't forget to checkout this branch after creating it. -3. Make your homework! -4. Once you're finished, add your homework to a commit. Make sure you *only* commit your homework files and nothing else. You can use `git add -p` if you only want to add a couple files. You can always check what is happening with the `git status` command (as one of our mentors always says, it is the console.log of git!). -5. Create the commit (`git commit`). Make the commit message meaningful, for example `finished project for homework week1`. +2. Create a new branch for each week you have assignments. For example, for the week 1 assignments for Node create a branch called `YOUR_NAME-w1-Node`. Don't forget to checkout this branch after creating it. +3. Make your assignments! +4. Once you're finished, add your assignments to a commit. Make sure you _only_ commit your assignments files and nothing else. You can use `git add -p` if you only want to add a couple files. You can always check what is happening with the `git status` command (as one of our mentors always says, it is the console.log of git!). +5. Create the commit (`git commit`). Make the commit message meaningful, for example `finished project for assignments week1`. 6. Push the branch to your forked repository 7. On the GitHub page of your forked repository, click on the `create pull request` button. Make sure the `base repository` is your teacher's repository, on branch master 8. Give the pull request a title in the following format: ```markdown -Homework week 1 +Assignments week 1 ``` 9. Submit the pull request from your forked repository branch into the `main` branch If you have any questions or if something is not entirely clear ¯\\\_(ツ)\_/¯, please ask/comment on Slack! - diff --git a/week1/LESSONPLAN.md b/week1/LESSONPLAN.md index c2af96a22..71d80fa8f 100644 --- a/week1/LESSONPLAN.md +++ b/week1/LESSONPLAN.md @@ -33,7 +33,7 @@ Node.js is a server-side platform, that allows us to use JavaScript to write bac Show students how to use Node.js to execute JavaScript files. Start with the following simple example, but explain how this log will be found inside of the command line instead of ```js -console.log('Hello World!'); +console.log("Hello World!"); ``` #### Exercise @@ -42,14 +42,14 @@ In a new JavaScript file write a function that returns true if a day is a weeken ```javascript function isWeekend(dayOfWeek) { - if (dayOfWeek === 'Saturday') return true; - if (dayOfWeek === 'Monday') return false; + if (dayOfWeek === "Saturday") return true; + if (dayOfWeek === "Monday") return false; // fill in the rest } -console.log('Tuesday is a ' + (isWeekend('Tuesday') ? 'weekend' : 'week day')); // week day -console.log('Friday is a ' + (isWeekend('Friday') ? 'weekend' : 'week day')); // week day -console.log('Sunday is a ' + (isWeekend('Sunday') ? 'weekend' : 'week day')); // weekend +console.log("Tuesday is a " + (isWeekend("Tuesday") ? "weekend" : "week day")); // week day +console.log("Friday is a " + (isWeekend("Friday") ? "weekend" : "week day")); // week day +console.log("Sunday is a " + (isWeekend("Sunday") ? "weekend" : "week day")); // weekend ``` Execute the file with `node` in the command line. @@ -153,11 +153,11 @@ In a new folder: Create a JavaScript file called `server.js`, with the following code: ```javascript -const express = require('express'); +const express = require("express"); const app = express(); const PORT = 3000; -app.get('/', (req, res) => res.send('Hello World!')); +app.get("/", (req, res) => res.send("Hello World!")); app.listen(PORT, () => console.log(`Example app listening on port ${PORT}!`)); ``` @@ -176,7 +176,7 @@ Write an Express app that serves the following HTML:

Things to do

    -
  • Write homework
  • +
  • Write assignments
  • Buy groceries
  • Prepare dinner
  • Watch movie
  • @@ -185,7 +185,6 @@ Write an Express app that serves the following HTML: ``` - #### Essence Express.js is used to easily create web servers, that allow us (among other reasons) to serve HTML so our browser can read it. The browser sends a request to a specific address, like `/`, and our server (through Express) responds with an HTML file. @@ -193,24 +192,31 @@ Express.js is used to easily create web servers, that allow us (among other reas ### 5. Serving static files with Express #### Explanation + Motiviation based on previous exercise where HTML code is put in the javascript. Instead of doing this, the HTML can be saved in a file in the project and then send with express when needed. #### Example + Save the HTML content in a new file in the project e.g. `index.html`. Then modify the javascript code to serve the HTML from the file instead of having it hardcoded. + ```javascript -const express = require('express'); +const express = require("express"); const app = express(); const PORT = 3000; -app.get('/', (req, res) => res.sendfile('index.html')); +app.get("/", (req, res) => res.sendfile("index.html")); app.listen(PORT, () => console.log(`Example app listening on port ${PORT}!`)); ``` #### Exercise + ```css -li:nth-child(even) {background-color: #CCC} +li:nth-child(even) { + background-color: #ccc; +} ``` + If time left: Save the above css in a file `style.css`. Link the stylesheet in the HTML file. Extend the express app to return the sylesheet for route `\style.css`. diff --git a/week1/MAKEME.md b/week1/MAKEME.md index 3ee560ac8..2b5fbf315 100644 --- a/week1/MAKEME.md +++ b/week1/MAKEME.md @@ -1,4 +1,4 @@ -# Homework Node.js Week 1 +# Assignments Node.js Week 1 ## Todo List @@ -46,7 +46,7 @@ Inside of your `Node.js` fork, go to the folder `week1`. Inside of that folder, ## **5. PROJECT: HackYourTemperature I** -> In this part of the homework you'll be setting up the basis of your project: `HackYourTemperature`. Inside the folder `homework`, create a new folder called `hackyourtemperature`. You'll add to it every week. +> In this part of the assignments you'll be setting up the basis of your project: `HackYourTemperature`. Inside the folder `assignments`, create a new folder called `hackyourtemperature`. You'll add to it every week. In this module you'll be building the simplest of API's, starting from scratch. @@ -76,9 +76,9 @@ If you are tired of constantly restarting your server, google the `nodemon` pack ## **SUBMIT YOUR HOMEWORK!** -After you've finished your todo list it's time to show us what you got! Have a look at the following [guide](../hand-in-homework-guide.md) to see how it's done. +After you've finished your todo list it's time to show us what you got! Have a look at the following [guide](../hand-in-assignments-guide.md) to see how it's done. -The homework that needs to be submitted is the following: +The assignments that needs to be submitted is the following: 1. Project: HackYourTemperature I diff --git a/week1/README.md b/week1/README.md index 43a94f301..3a825e738 100644 --- a/week1/README.md +++ b/week1/README.md @@ -44,7 +44,7 @@ It is time to start practicing interviews as it is a big part of the hiring proc You don't have to do all of this at this moment. This week you will get a message in your class channel about when the Career Training 2 session will be. _Before_ that session make sure to have: - Read the whole [‘Interview Preparation’ Repo](https://github.com/HackYourFuture/interviewpreparation). -- Done the homework: make a copy of [this file](https://docs.google.com/document/u/2/d/114rTGS4eG6tpkrMAyVIdvgTrnpmkRL6ax_smkw1B0HI/copy) and submit your answers to the team [here](https://hackyourfuture.typeform.com/to/s6zYAugm). +- Done the assignments: make a copy of [this file](https://docs.google.com/document/u/2/d/114rTGS4eG6tpkrMAyVIdvgTrnpmkRL6ax_smkw1B0HI/copy) and submit your answers to the team [here](https://hackyourfuture.typeform.com/to/s6zYAugm). ## Extra reading diff --git a/week2/MAKEME.md b/week2/MAKEME.md index f340f50b8..dcb4d07c7 100644 --- a/week2/MAKEME.md +++ b/week2/MAKEME.md @@ -1,4 +1,4 @@ -# Homework Node.js Week 2 +# Assignments Node.js Week 2 ## Todo List @@ -25,7 +25,7 @@ Inside of your `Node.js` fork, go to the folder `week2`. Inside of that folder, So far you've build a basic web server. We've loaded in the necessary modules. We have an `end point`, which is `/`. We have activated the server, by `listening` to a port number. And we have created a `POST` request to handle input from the user. -This week's homework we will expand on that, in 2 parts: +This week's assignments we will expand on that, in 2 parts: 1. We will connect our API to an external API to grab the data we want. 2. We are going to add tests to our API to ensure that it works as intended. @@ -143,7 +143,7 @@ Enjoy! Remember that the Career Training 2 session is coming up (check your class channel on slack for the exact date). Before the session make sure you have: - Read the whole [‘Interview Preparation’ Repo](https://github.com/HackYourFuture/interviewpreparation). -- Done the homework: make a copy of [this file](https://docs.google.com/document/u/2/d/114rTGS4eG6tpkrMAyVIdvgTrnpmkRL6ax_smkw1B0HI/copy) and submit your answers to the team [here](https://hackyourfuture.typeform.com/to/s6zYAugm). +- Done the assignments: make a copy of [this file](https://docs.google.com/document/u/2/d/114rTGS4eG6tpkrMAyVIdvgTrnpmkRL6ax_smkw1B0HI/copy) and submit your answers to the team [here](https://hackyourfuture.typeform.com/to/s6zYAugm). ## **6. Optional: Side project ideas** @@ -169,9 +169,9 @@ We focused solely on the REST way of building an API, but there is a different w After you've finished your todo list it's time to show us what you got! Upload all your files to your forked repository (same as week 1). Then make a pull request to it. -If you need a refresher, take a look at the following [guide](../hand-in-homework-guide.md) to see how it's done. +If you need a refresher, take a look at the following [guide](../hand-in-assignments-guide.md) to see how it's done. -The homework that needs to be submitted is the following: +The assignments that needs to be submitted is the following: 1. Project: HackYourTemperature II From f41c0f59d3e722806aa8aaa19c6eacacda96158a Mon Sep 17 00:00:00 2001 From: JosephineHYF <113513079+JosephineHYF@users.noreply.github.com> Date: Mon, 13 Nov 2023 10:00:57 +0100 Subject: [PATCH 12/31] Update README.md From 6f80b7f1dd08c71d500572be1dd92e51ff0b7671 Mon Sep 17 00:00:00 2001 From: JosephineHYF <113513079+JosephineHYF@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:56:53 +0100 Subject: [PATCH 13/31] Update README.md --- week2/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/week2/README.md b/week2/README.md index 5fd499af6..72f46295b 100644 --- a/week2/README.md +++ b/week2/README.md @@ -13,6 +13,8 @@ 4. [Automated API testing](https://study.hackyourfuture.net/#/testing/api-testing.md) - [Postman](https://www.postman.com/automated-testing/) - [supertest](https://www.npmjs.com/package/supertest) +5. [Interview preparation](https://github.com/HackYourFuture/interviewpreparation) + ## 0. Video Lectures From 616d6f325cb4582d3cbdb60bc720fe2ffa21ac29 Mon Sep 17 00:00:00 2001 From: JosephineHYF <113513079+JosephineHYF@users.noreply.github.com> Date: Tue, 12 Dec 2023 16:03:38 +0100 Subject: [PATCH 14/31] Update README.md --- week2/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/week2/README.md b/week2/README.md index 72f46295b..d7b06e8dc 100644 --- a/week2/README.md +++ b/week2/README.md @@ -39,6 +39,9 @@ We will also look into enhancing your API. One thing to keep in mind that your o Lastly, it is time to learn how to automate the testing of our API's. This can be done in Postman using [automated testsuites](https://www.postman.com/use-cases/api-testing-automation/) but we are going to do it using code, similar to unit testing learned in JavaScript. Have a look [here](https://study.hackyourfuture.net/#/testing/api-testing.md) on how to do that using the [supertest](https://www.npmjs.com/package/supertest) library. +## Career training II (Interview preparation) +If you haven't finished all the material yet, then continue with it this week. + ## Finished? Are you finished with going through the materials? High five! If you feel ready to get practical, click [here](./MAKEME.md). From cd41e349da8049d9e79490be7d67f99f7cddfdd7 Mon Sep 17 00:00:00 2001 From: JosephineHYF <113513079+JosephineHYF@users.noreply.github.com> Date: Tue, 12 Dec 2023 16:37:45 +0100 Subject: [PATCH 15/31] Update README.md --- week2/README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/week2/README.md b/week2/README.md index d7b06e8dc..72f46295b 100644 --- a/week2/README.md +++ b/week2/README.md @@ -39,9 +39,6 @@ We will also look into enhancing your API. One thing to keep in mind that your o Lastly, it is time to learn how to automate the testing of our API's. This can be done in Postman using [automated testsuites](https://www.postman.com/use-cases/api-testing-automation/) but we are going to do it using code, similar to unit testing learned in JavaScript. Have a look [here](https://study.hackyourfuture.net/#/testing/api-testing.md) on how to do that using the [supertest](https://www.npmjs.com/package/supertest) library. -## Career training II (Interview preparation) -If you haven't finished all the material yet, then continue with it this week. - ## Finished? Are you finished with going through the materials? High five! If you feel ready to get practical, click [here](./MAKEME.md). From fb97a46a198f4d09a52b447889a81b39ec8eca06 Mon Sep 17 00:00:00 2001 From: JosephineHYF <113513079+JosephineHYF@users.noreply.github.com> Date: Tue, 12 Dec 2023 16:42:27 +0100 Subject: [PATCH 16/31] Update README.md --- week1/README.md | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/week1/README.md b/week1/README.md index 3a825e738..373338d13 100644 --- a/week1/README.md +++ b/week1/README.md @@ -17,9 +17,9 @@ These are the topics for week 1: 1. [How does the internet work?](https://study.hackyourfuture.net/#/the-internet/) -## 0. Video's +## Videos -Your teacher Andrej has made some videos for this week's material that complements the reading material. You can find them here: [Videos 1 - 6](https://www.youtube.com/playlist?list=PLVYDhqbgYpYXpc_l_Vlj8yz3LjgkkWXnn) (up to and including the Express example) +Your mentor Andrej has made some videos for this week's material that complements the reading material. You can find them here: [Videos 1 - 6](https://www.youtube.com/playlist?list=PLVYDhqbgYpYXpc_l_Vlj8yz3LjgkkWXnn) (up to and including the Express example) HYF Video @@ -29,26 +29,15 @@ This week we are going to have our first meeting with the backend and learn how Let's first take a broad look at what the term backend really means by reading up on it [here](https://study.hackyourfuture.net/#/software-development/backend.md). A common used term when looking at the frontend/backend ecosystem is the client-server model, read up about that [here](https://study.hackyourfuture.net/#/definitions/client-server-model.md). It would also be good to dive a little deeper into the communication protocol that is used in the internet by looking into the [http protocol](https://study.hackyourfuture.net/#/the-internet/http.md) and the [http methods](https://study.hackyourfuture.net/#/the-internet/http-methods) that are used to communicate certain actions. -Now that we have gotten the big picture it is time to see how we can build these things. To be able to use JavaScript on the backend, Node.js was created. Have a good look at what Node.js is [here](https://study.hackyourfuture.net/#/node-js/). +Now that we have gotten the big picture, it is time to see how we can build these things. To be able to use JavaScript on the backend, Node.js was created. Have a good look at what Node.js is [here](https://study.hackyourfuture.net/#/node-js/). In the Node.js page you have read about different modules (or packages as some call them) and we will be using one of these called **Express.js** with which we can easily create web servers. The express.js package has become an industry standard that is widely used. Have a look what it does [here](https://study.hackyourfuture.net/#/node-js/express-js). Lastly, testing your API's without a frontend is a little cumbersome. Have a look at the tool Postman that is used a lot to test out API's [here](https://study.hackyourfuture.net/#/tools/postman.md) -## Career training 2 (Interview preparation) - -It is time to start practicing interviews as it is a big part of the hiring process. The [interview preparation repository](https://github.com/HackYourFuture/interviewpreparation) goes over the different types of interviews you will have at companies as well as interviews that you will have upcoming during the curriculum. - -### Career training 2: Planning - -You don't have to do all of this at this moment. This week you will get a message in your class channel about when the Career Training 2 session will be. _Before_ that session make sure to have: - -- Read the whole [‘Interview Preparation’ Repo](https://github.com/HackYourFuture/interviewpreparation). -- Done the assignments: make a copy of [this file](https://docs.google.com/document/u/2/d/114rTGS4eG6tpkrMAyVIdvgTrnpmkRL6ax_smkw1B0HI/copy) and submit your answers to the team [here](https://hackyourfuture.typeform.com/to/s6zYAugm). - ## Extra reading -If you have time left over this week (or any week this module) then it is a good idea to look at the topic of 'the internet' and learn how it works [here](https://study.hackyourfuture.net/#/the-internet/). This will be valuable when you get into the more complex applications and may also help your understanding of the big picture. +If you have time left over this week (or any week this module), then it is a good idea to look at the topic of 'the internet' and learn how it works [here](https://study.hackyourfuture.net/#/the-internet/). This will be valuable when you get into the more complex applications and may also help your understanding of the big picture. ## Finished? From c5edf4245a0248541e97ac606937be0e7840fbab Mon Sep 17 00:00:00 2001 From: JosephineHYF <113513079+JosephineHYF@users.noreply.github.com> Date: Tue, 12 Dec 2023 16:47:42 +0100 Subject: [PATCH 17/31] Update README.md --- week2/README.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/week2/README.md b/week2/README.md index 72f46295b..bb53f18b1 100644 --- a/week2/README.md +++ b/week2/README.md @@ -13,12 +13,12 @@ 4. [Automated API testing](https://study.hackyourfuture.net/#/testing/api-testing.md) - [Postman](https://www.postman.com/automated-testing/) - [supertest](https://www.npmjs.com/package/supertest) -5. [Interview preparation](https://github.com/HackYourFuture/interviewpreparation) +5. Career Training II: [Interview preparation](https://github.com/HackYourFuture/interviewpreparation) ## 0. Video Lectures -Your teacher Andrej has made video lectures for this week's material that complements the reading material. You can find them here: [Videos 7 - 10](https://www.youtube.com/playlist?list=PLVYDhqbgYpYXpc_l_Vlj8yz3LjgkkWXnn) +Your mentor Andrej has made video lectures for this week's material that complements the reading material. You can find them here: [Videos 7 - 10](https://www.youtube.com/playlist?list=PLVYDhqbgYpYXpc_l_Vlj8yz3LjgkkWXnn) HYF Video @@ -33,12 +33,23 @@ You might have noticed that the four CRUD actions nicely align with the HTTP met 3. Update -> PUT 4. Delete -> DELETE -Having covered these terms we can now look into one of the most common API architectures, the REST API. Have a look at the explanation of this design [here](https://study.hackyourfuture.net/#/the-internet/designing-apis.md). +Having covered these terms, we can now look into one of the most common API architectures, the REST API. Have a look at the explanation of this design [here](https://study.hackyourfuture.net/#/the-internet/designing-apis.md). We will also look into enhancing your API. One thing to keep in mind that your own API can make use of other API's for certain functionality! In fact, this happens all the time and is a great way to split the separation of concerns. Have a look at how this works [here](https://study.hackyourfuture.net/#/node-js/consuming-apis.md). Lastly, it is time to learn how to automate the testing of our API's. This can be done in Postman using [automated testsuites](https://www.postman.com/use-cases/api-testing-automation/) but we are going to do it using code, similar to unit testing learned in JavaScript. Have a look [here](https://study.hackyourfuture.net/#/testing/api-testing.md) on how to do that using the [supertest](https://www.npmjs.com/package/supertest) library. +## Career Training II: interview preparation + +It is time to start practicing interviews as it is a crucial part of the hiring process. The [interview preparation repository](https://github.com/HackYourFuture/interviewpreparation) goes over the different types of interviews you will go through with companies as well as the ones that you will have to endure during the curriculum. + +### Career Training II: planning + +You don't have to do all of this immediately. This week you will receive a message from Giuseppina in your cohort channel to schedule the Career Training II session. _Before_ that session, make sure to have: + +- Read the whole [‘Interview Preparation’ Repo](https://github.com/HackYourFuture/interviewpreparation). +- Done the assignments: make a copy of [this file](https://docs.google.com/document/u/2/d/114rTGS4eG6tpkrMAyVIdvgTrnpmkRL6ax_smkw1B0HI/copy) and submit your answers to the team [here](https://hackyourfuture.typeform.com/to/s6zYAugm). + ## Finished? Are you finished with going through the materials? High five! If you feel ready to get practical, click [here](./MAKEME.md). From 4c42274b4326678ce0703f8b7593c9a68f5ee756 Mon Sep 17 00:00:00 2001 From: JosephineHYF <113513079+JosephineHYF@users.noreply.github.com> Date: Tue, 12 Dec 2023 17:41:20 +0100 Subject: [PATCH 18/31] Update README.md --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 93eee5aba..8181a1c4e 100644 --- a/README.md +++ b/README.md @@ -16,20 +16,20 @@ During the following 2 weeks you'll be learning all about this. As a tool to ill ## Learning goals -In this module you will get familiar with the world of backend development. By the end of it you have learned: +In this module you get familiar with the world of backend development. By the end of it, you have learned: -- What is meant by the term `backend` -- The `client-server` model -- What `HTTP` and `REST` mean -- How to `create your own web servers` with Node.js, using `Express.js` -- What a `templating engine` is. -- How to use the `Node Package Manager (NPM)`. -- How to use Express.js to make a `RESTful API` -- How to build a small `full-stack application` +- What is meant by the term `backend`; +- The `client-server` model; +- What `HTTP` and `REST` mean; +- How to `create your own web servers` with Node.js, using `Express.js`; +- What a `templating engine` is; +- How to use the `Node Package Manager (NPM)`; +- How to use Express.js to make a `RESTful API`; +- How to build a small `full-stack application`. ## Before you start -Before you start you need to install a very important software: Node.js! We're going to use the latest stable version of it, which is **v16.x**. Click on the following link to download it to your computer: +Before you start you need to install a very important software: Node.js! We're using the latest stable version of it, which is **v16.x**. Click on the following link to download it to your computer: - For [Ubuntu](https://github.com/nodesource/distributions#debinstall) - For [macOS](https://nodejs.org/en/download/) @@ -69,7 +69,7 @@ To have a more detailed overview of the guidelines, please read [this document]( ### Video lectures -For each module HackYourFuture provides you with video lectures. These are made by experienced software developers who know what they're talking about. The main teacher for this module will be [Andrej Gajduk](https://hackyourfuture.slack.com/team/UL0P2MB52): Product Owner and Senior Full-Stack Developer! +For each module, HackYourFuture provides you with video lectures. These are made by experienced software developers who know what they're talking about. The main mentor for this module is [Andrej Gajduk](https://hackyourfuture.slack.com/team/UL0P2MB52): Product Owner and Senior Full-Stack Developer! You can find out more about him here: From da044c0af3736ba63ce42dfb57ea8e8f47a5bade Mon Sep 17 00:00:00 2001 From: stasel <2033301+stasel@users.noreply.github.com> Date: Tue, 23 Jan 2024 13:30:16 +0100 Subject: [PATCH 19/31] Added week 3 placeholder --- README.md | 1 + assets/nodejs.png | Bin 37739 -> 36787 bytes 2 files changed, 1 insertion(+) diff --git a/README.md b/README.md index 8181a1c4e..7abacd65b 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,7 @@ Learn from Andrej in the following playlist of videos he has made for you! (Clic | ---: | ----------------------------------- | ------------------------------ | --------------------------------- | ------------------------------------- | | 1. | Client-server model, HTTP & Express | [Readings W1](week1/README.md) | [Assignments W1](week1/MAKEME.md) | [Lesson Plan W1](week1/LESSONPLAN.md) | | 2. | REST, CRUD, API calls, Testing | [Readings W2](week2/README.md) | [Assignments W2](week2/MAKEME.md) | [Lesson Plan W2](week2/LESSONPLAN.md) | +| 3. | ~Work in progress~ | [Readings W3](week3/README.md) | | [Lesson Plan W3](week3/LESSONPLAN.md) | ## Finished? diff --git a/assets/nodejs.png b/assets/nodejs.png index 3501f481b1d85641427f3e37fbdbb593aa9cc513..c89e275cb790bcac220abeae4311cd25aaf95041 100644 GIT binary patch literal 36787 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 KRw88_^uGW&SzBoU literal 37739 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 From f85a7eb327af7199cf2bd80d80798afa279f8e1d Mon Sep 17 00:00:00 2001 From: stasel <2033301+stasel@users.noreply.github.com> Date: Tue, 23 Jan 2024 13:32:54 +0100 Subject: [PATCH 20/31] Fixed week 3 formatting --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7abacd65b..045a7a069 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Learn from Andrej in the following playlist of videos he has made for you! (Clic | ---: | ----------------------------------- | ------------------------------ | --------------------------------- | ------------------------------------- | | 1. | Client-server model, HTTP & Express | [Readings W1](week1/README.md) | [Assignments W1](week1/MAKEME.md) | [Lesson Plan W1](week1/LESSONPLAN.md) | | 2. | REST, CRUD, API calls, Testing | [Readings W2](week2/README.md) | [Assignments W2](week2/MAKEME.md) | [Lesson Plan W2](week2/LESSONPLAN.md) | -| 3. | ~Work in progress~ | [Readings W3](week3/README.md) | | [Lesson Plan W3](week3/LESSONPLAN.md) | +| 3. | == Work in progress == | [Readings W3](week3/README.md) | | [Lesson Plan W3](week3/LESSONPLAN.md) | ## Finished? From b9680b44725ef068750675e6e4e5f4d9e2cf2b42 Mon Sep 17 00:00:00 2001 From: stasel <2033301+stasel@users.noreply.github.com> Date: Fri, 2 Feb 2024 17:14:12 +0100 Subject: [PATCH 21/31] Added build-with-students example code --- week3/build-with-students/app.js | 16 +++ week3/build-with-students/package.json | 18 ++++ week3/build-with-students/users.js | 134 +++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 week3/build-with-students/app.js create mode 100644 week3/build-with-students/package.json create mode 100644 week3/build-with-students/users.js diff --git a/week3/build-with-students/app.js b/week3/build-with-students/app.js new file mode 100644 index 000000000..cd01d6424 --- /dev/null +++ b/week3/build-with-students/app.js @@ -0,0 +1,16 @@ +import express from 'express'; +import { register, login, getProfile, logout } from './users.js'; + +let app = express(); + +app.use(express.json()); + +app.post ( "/register", register); +app.post ( "/login", login); +app.post ( "/logout", logout); +app.get ( "/profile", getProfile); + + +app.listen(3000, () => { + console.log('Server is running on port 3000'); +}); diff --git a/week3/build-with-students/package.json b/week3/build-with-students/package.json new file mode 100644 index 000000000..a6891a78d --- /dev/null +++ b/week3/build-with-students/package.json @@ -0,0 +1,18 @@ +{ + "name": "build-with-students", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "type": "module", + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "bcrypt": "^5.1.1", + "express": "^4.18.2", + "uuid": "^9.0.1" + } +} diff --git a/week3/build-with-students/users.js b/week3/build-with-students/users.js new file mode 100644 index 000000000..b591591a9 --- /dev/null +++ b/week3/build-with-students/users.js @@ -0,0 +1,134 @@ +import { v4 as generateUUID } from 'uuid'; +import { hash, compare } from 'bcrypt'; + +// The higher the number, the more secure password but also slower the hashing process. +const saltRounds = 10; + +const usersDatabase = [{ + id: "9a2cd641-3bc5-4334-9813-926543e08426", + username: "alice1", + password: "$2b$10$Coxs6k4gAnMIKI1wcgpRpOVHSD7um5QaIoHBCqfeLHu6yYRqyRbcm" // 1234 +}]; + +let sessions = [ + { + token: "13422ece-d321-49d8-8f8f-11cb6d67287f", + userId: "9a2cd641-3bc5-4334-9813-926543e08426" + } +]; + +export const register = async (req, res) => { + // Check request body + if (!req.body.username || !req.body.password) { + res.status(400).json({ message: 'Please provide username and password' }).end(); + return; + } + + // Check if username already exists + const isUsernameExists = getUserByUsername(req.body.username) !== undefined; + if (isUsernameExists) { + res.status(400).json({ message: 'Username already exists' }).end(); + return; + } + + // Hash the password and create new user + const hashedPassword = await hash(req.body.password, saltRounds); + const newUser = { + id: generateUUID(), + username: req.body.username, + password: hashedPassword, + }; + + // Save user to usersDatabase + usersDatabase.push(newUser); + + // Return success and the new user to the client + res.status(201).json({ + id: newUser.id, + username: newUser.username, + }).end(); +}; + + +export const login = async (req, res) => { + // Check request body + if (!req.body.username || !req.body.password) { + res.status(400).json({ message: 'Please provide username and password' }).end(); + return; + } + + // Find user in the database + const user = getUserByUsername(req.body.username); + if (!user) { + res.status(401).json({ message: 'Invalid username / password combination' }).end(); + return; + } + + // Check if password is correct by using bcrypt compare + const isPasswordCorrect = await compare(req.body.password, user.password); + if (!isPasswordCorrect) { + res.status(401).json({ message: 'Invalid username / password combination' }).end(); + return; + } + + // Login successfully - create a session token + const token = generateUUID(); + + // Save the session token + sessions.push({ token, userId: user.id }); + + // Return the token to the client + // The client should save the token and send it in the Authorization header for future requests: + // Authorization: bearer + res.status(200).json({ token }).end(); +}; + + +export const getProfile = (req, res) => { + // Check if user is logged in + const token = extractBearerTokenFromAuth(req.headers.authorization); + const session = getSessionByToken(token); + if (!session) { + res.status(401).json({ message: 'You are not logged in' }).end(); + return; + } + + // Get user details from the session + const user = getUserById(session.userId); + if (!user) { + res.status(401).json({ message: 'You are not logged in' }).end(); + return; + } + + // Return a message with the username + res.status(200).json({ message: `Hello! You are currently logged in as ${user.username}!` }).end(); +}; + +export const logout = (req, res) => { + const token = extractBearerTokenFromAuth(req.headers.authorization); + + // remove the session from the sessions array + sessions = sessions.filter(session => session.token !== token); + res.status(204).end(); +}; + + +// Helper functions +const getUserByUsername = (username) => { + return usersDatabase.find(user => user.username === username); +}; + +const getUserById = (userID) => { + return usersDatabase.find(user => user.id === userID); +}; + +const getSessionByToken = (token) => { + return sessions.find(session => session.token === token); +}; + +const extractBearerTokenFromAuth = (authorization) => { + if (!authorization || !authorization.startsWith('Bearer ')) { + return null; + } + return authorization.replace('Bearer ', ''); +} \ No newline at end of file From 03881153c4f76cff0f9f50d56dc6f785f31257a2 Mon Sep 17 00:00:00 2001 From: stasel <2033301+stasel@users.noreply.github.com> Date: Sun, 4 Feb 2024 15:20:13 +0100 Subject: [PATCH 22/31] Added content to NodeJS Week 3 --- README.md | 4 +- week2/README.md | 7 +-- .../{3-party-time => 2-party-time}/README.md | 0 .../{3-party-time => 2-party-time}/script.js | 0 week3/LESSONPLAN.md | 17 +++++++ week3/MAKEME.md | 50 +++++++++++++++++++ week3/README.md | 41 +++++++++++++++ .../1-basic-authentication}/README.md | 2 +- .../1-basic-authentication}/script.js | 0 .../2-bcrypt-examples/README.md | 3 ++ .../practice-exercises/3-jwt-tokens/README.md | 3 ++ 11 files changed, 118 insertions(+), 9 deletions(-) rename week2/practice-exercises/{3-party-time => 2-party-time}/README.md (100%) rename week2/practice-exercises/{3-party-time => 2-party-time}/script.js (100%) create mode 100644 week3/LESSONPLAN.md create mode 100644 week3/MAKEME.md create mode 100644 week3/README.md rename {week2/practice-exercises/2-authentication => week3/practice-exercises/1-basic-authentication}/README.md (98%) rename {week2/practice-exercises/2-authentication => week3/practice-exercises/1-basic-authentication}/script.js (100%) create mode 100644 week3/practice-exercises/2-bcrypt-examples/README.md create mode 100644 week3/practice-exercises/3-jwt-tokens/README.md diff --git a/README.md b/README.md index 045a7a069..c0b709052 100644 --- a/README.md +++ b/README.md @@ -86,8 +86,8 @@ Learn from Andrej in the following playlist of videos he has made for you! (Clic | Week | Topic | Readings | Assignments | Lesson Plan | | ---: | ----------------------------------- | ------------------------------ | --------------------------------- | ------------------------------------- | | 1. | Client-server model, HTTP & Express | [Readings W1](week1/README.md) | [Assignments W1](week1/MAKEME.md) | [Lesson Plan W1](week1/LESSONPLAN.md) | -| 2. | REST, CRUD, API calls, Testing | [Readings W2](week2/README.md) | [Assignments W2](week2/MAKEME.md) | [Lesson Plan W2](week2/LESSONPLAN.md) | -| 3. | == Work in progress == | [Readings W3](week3/README.md) | | [Lesson Plan W3](week3/LESSONPLAN.md) | +| 2. | REST, CRUD, API calls | [Readings W2](week2/README.md) | [Assignments W2](week2/MAKEME.md) | [Lesson Plan W2](week2/LESSONPLAN.md) | +| 3. | User Authentication, session management, Testing | [Readings W3](week3/README.md) | [Assignments W3](week3/MAKEME.md) | [Lesson Plan W3](week3/LESSONPLAN.md) | ## Finished? diff --git a/week2/README.md b/week2/README.md index bb53f18b1..144da75b8 100644 --- a/week2/README.md +++ b/week2/README.md @@ -10,10 +10,7 @@ 3. [Making use of other APIs](https://study.hackyourfuture.net/#/node-js/consuming-apis.md) - How to consume an external API? - Example of middleware -4. [Automated API testing](https://study.hackyourfuture.net/#/testing/api-testing.md) - - [Postman](https://www.postman.com/automated-testing/) - - [supertest](https://www.npmjs.com/package/supertest) -5. Career Training II: [Interview preparation](https://github.com/HackYourFuture/interviewpreparation) +4. Career Training II: [Interview preparation](https://github.com/HackYourFuture/interviewpreparation) ## 0. Video Lectures @@ -37,8 +34,6 @@ Having covered these terms, we can now look into one of the most common API arch We will also look into enhancing your API. One thing to keep in mind that your own API can make use of other API's for certain functionality! In fact, this happens all the time and is a great way to split the separation of concerns. Have a look at how this works [here](https://study.hackyourfuture.net/#/node-js/consuming-apis.md). -Lastly, it is time to learn how to automate the testing of our API's. This can be done in Postman using [automated testsuites](https://www.postman.com/use-cases/api-testing-automation/) but we are going to do it using code, similar to unit testing learned in JavaScript. Have a look [here](https://study.hackyourfuture.net/#/testing/api-testing.md) on how to do that using the [supertest](https://www.npmjs.com/package/supertest) library. - ## Career Training II: interview preparation It is time to start practicing interviews as it is a crucial part of the hiring process. The [interview preparation repository](https://github.com/HackYourFuture/interviewpreparation) goes over the different types of interviews you will go through with companies as well as the ones that you will have to endure during the curriculum. diff --git a/week2/practice-exercises/3-party-time/README.md b/week2/practice-exercises/2-party-time/README.md similarity index 100% rename from week2/practice-exercises/3-party-time/README.md rename to week2/practice-exercises/2-party-time/README.md diff --git a/week2/practice-exercises/3-party-time/script.js b/week2/practice-exercises/2-party-time/script.js similarity index 100% rename from week2/practice-exercises/3-party-time/script.js rename to week2/practice-exercises/2-party-time/script.js diff --git a/week3/LESSONPLAN.md b/week3/LESSONPLAN.md new file mode 100644 index 000000000..2169630ee --- /dev/null +++ b/week3/LESSONPLAN.md @@ -0,0 +1,17 @@ +# Node.js Week 3 (Lesson Plan) + +## Agenda + +1. Authentication +2. User registration and using bcrypt to store passwords. +3. Login, protected endpoints and logout +4. Testing + +## Core concepts + +FIRST HALF (12.00 - 13.30) + +**Example** + +**Exercise** + diff --git a/week3/MAKEME.md b/week3/MAKEME.md new file mode 100644 index 000000000..b50f7c1f4 --- /dev/null +++ b/week3/MAKEME.md @@ -0,0 +1,50 @@ +# Assignments Node.js Week 3 + +## Todo List + +1. Prep exercises +2. Practice exercises +3. Optional: Side project ideas + +## **1. Prep exercises** + +> Prep exercises are exercises that you should work on _before_ the session on Sunday. These are a little more difficult or show an important concept and as such are a great exercise to talk about with your mentor. Have a solution ready by Sunday as you may be asked to show what you did. + +Inside your `Node.js` fork, go to the folder `week3`. Inside of that folder, navigate to `/prep-exercises`. For each exercise, you will find a separate folder. The `README` explains what needs to be done. There will also be some questions at the bottom to think about. Go through them _before_ the session on Sunday as it will be covered then. + +## **2. Practice exercises** + +Inside of your `Node.js` fork, go to the folder `week3`. Inside of that folder, navigate to `/practice-exercises`. For each exercise, you will find a separate folder. The `README` explains what needs to be done. Go through them to practice concepts that you have learned about! + + +## **3. Optional: Side project ideas** + +> A part of the HackYourFuture curriculum is to work on as many side projects as you can throughout the time you have. This is a nice way to add extra knowledge to your arsenal and show in your CV that you are motivated to learn new technologies. There are plenty of people available to help you out in the `#get-help` channel on Slack so definitely make use of that! Have a look at the [hyf_projects repo](https://github.com/HackYourFuture/hyf_projects/blob/main/README.md#project-2-a-try-out-application) for more details. + +### 3.1 Document your API! + +When using API's in the `Using API's` module you will have noticed that those API's all have extensive documentation on how to use it. As developers like to build tools for everything there are quite a few good tools to semi-automatically document your API from your code! Saves a lot of work and makes sure that you don't forget to update the documentation if the code changes! + +Add automatic documentation to your API by using one of these tools (Swagger, apiDoc or docbox)! + +### 3.2 Web Sockets + +It is becoming normal that all webpages automatically refresh whenever there is new data available. Think about the live news feeds that tell you when there is a new item, or that there is a new message on twitter. This is all implemented using Web Sockets, where you as a programmer can set up a link between your page and the server. + +Have a go by building a simple full stack chat application with an express websocket server! + +### 3.3 GraphQL + +We focused solely on the REST way of building an API, but there is a different way called `GraphQL`. This allows the frontend to define in their query the data that they want to get back. Very cool, but also quite complex. If you are up for a challenge, try to recreate your project using GraphQL (`express-graphql` package is probably the easiest way)! + +## **SUBMIT YOUR HOMEWORK!** + +After you've finished your todo list it's time to show us what you got! Upload all your files to your forked repository (same as week 1). Then make a pull request to it. + +If you need a refresher, take a look at the following [guide](../hand-in-assignments-guide.md) to see how it's done. + +The assignments that needs to be submitted is the following: + +1. Project: HackYourTemperature II + +_Deadline Tuesday 23.59 CET_ diff --git a/week3/README.md b/week3/README.md new file mode 100644 index 000000000..86e34ea9d --- /dev/null +++ b/week3/README.md @@ -0,0 +1,41 @@ +# Reading Material Node.js Week 3 + +## Agenda + +1. [What is authentication?](https://study.hackyourfuture.net/#/node-js/authentication.md) + +2. New user registration + - [Adding users to our application](https://study.hackyourfuture.net/#/node-js/user-registration.md) + - [How to securely store user passwords](https://study.hackyourfuture.net/#/node-js/storing-passwords.md) + +2. [Session management](https://study.hackyourfuture.net/#/node-js/session-management) + - Login and session tokens: + - Opaque token + - JSON Web Token (JWT) + - The `Authorization` header + - Protected endpoints + - Logout + +4. [Automated API testing](https://study.hackyourfuture.net/#/testing/api-testing.md) + - [Postman](https://www.postman.com/automated-testing/) + - [supertest](https://www.npmjs.com/package/supertest) + + +## Week goals + +This week we are going to learn about one of the most common tasks for any multi user application - `Authentication`. User authentication consists of new user registration, login, logout and identifying the currently logged in user in our API. + +You may have noticed a common trend when visiting websites that require you to sign up: + +1. **Registration** - creating a new user +2. **Login** - sending your credentials to enter the website. +3. **Accessing protected resources** - getting access to a special place in the website that only you can access (ex: shopping card, profile page) +4. **Logout** - Stop using the website. + +We will learn how to implement user regsitration and securtly store user passwords. We will also learn how to implement a login endpoint and check if the provided username / password combination is correct. Lastly, we will implemenet a special endpoint that can be only accessible to a user who previously logged in. + +Lastly, it is time to learn how to automate the testing of our API's. This can be done in Postman using [automated testsuites](https://www.postman.com/use-cases/api-testing-automation/) but we are going to do it using code, similar to unit testing learned in JavaScript. Have a look [here](https://study.hackyourfuture.net/#/testing/api-testing.md) on how to do that using the [supertest](https://www.npmjs.com/package/supertest) library. + +## Finished? + +Are you finished with going through the materials? High five! If you feel ready to get practical, click [here](./MAKEME.md). diff --git a/week2/practice-exercises/2-authentication/README.md b/week3/practice-exercises/1-basic-authentication/README.md similarity index 98% rename from week2/practice-exercises/2-authentication/README.md rename to week3/practice-exercises/1-basic-authentication/README.md index 1a84fe902..6e2ef4f29 100644 --- a/week2/practice-exercises/2-authentication/README.md +++ b/week3/practice-exercises/1-basic-authentication/README.md @@ -1,4 +1,4 @@ -# Authentication +# Basic Authentication So far all the APIs we used would happily respond to any request. In reality, most APIs hold sensitive information that should not be accessible for everyone. diff --git a/week2/practice-exercises/2-authentication/script.js b/week3/practice-exercises/1-basic-authentication/script.js similarity index 100% rename from week2/practice-exercises/2-authentication/script.js rename to week3/practice-exercises/1-basic-authentication/script.js diff --git a/week3/practice-exercises/2-bcrypt-examples/README.md b/week3/practice-exercises/2-bcrypt-examples/README.md new file mode 100644 index 000000000..81c99979c --- /dev/null +++ b/week3/practice-exercises/2-bcrypt-examples/README.md @@ -0,0 +1,3 @@ +# Bcrypt experiments + +TBD \ No newline at end of file diff --git a/week3/practice-exercises/3-jwt-tokens/README.md b/week3/practice-exercises/3-jwt-tokens/README.md new file mode 100644 index 000000000..b2c8165be --- /dev/null +++ b/week3/practice-exercises/3-jwt-tokens/README.md @@ -0,0 +1,3 @@ +# JWT Token experiments + +TBD \ No newline at end of file From 27bbf71435e6ebb6c5503a32b20531ef0c43c05f Mon Sep 17 00:00:00 2001 From: stasel <2033301+stasel@users.noreply.github.com> Date: Thu, 8 Feb 2024 23:13:53 +0100 Subject: [PATCH 23/31] Completed week 3 --- week3/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/week3/README.md b/week3/README.md index 86e34ea9d..9096b3bba 100644 --- a/week3/README.md +++ b/week3/README.md @@ -8,15 +8,15 @@ - [Adding users to our application](https://study.hackyourfuture.net/#/node-js/user-registration.md) - [How to securely store user passwords](https://study.hackyourfuture.net/#/node-js/storing-passwords.md) -2. [Session management](https://study.hackyourfuture.net/#/node-js/session-management) - - Login and session tokens: - - Opaque token - - JSON Web Token (JWT) +3. [Session management](https://study.hackyourfuture.net/#/node-js/session-management) + - Login and session tokens - The `Authorization` header - Protected endpoints - Logout -4. [Automated API testing](https://study.hackyourfuture.net/#/testing/api-testing.md) +4. [JSON Web Tokens](https://study.hackyourfuture.net/#/node-js/jwt-tokens.md) + +5. [Automated API testing](https://study.hackyourfuture.net/#/testing/api-testing.md) - [Postman](https://www.postman.com/automated-testing/) - [supertest](https://www.npmjs.com/package/supertest) @@ -32,7 +32,7 @@ You may have noticed a common trend when visiting websites that require you to s 3. **Accessing protected resources** - getting access to a special place in the website that only you can access (ex: shopping card, profile page) 4. **Logout** - Stop using the website. -We will learn how to implement user regsitration and securtly store user passwords. We will also learn how to implement a login endpoint and check if the provided username / password combination is correct. Lastly, we will implemenet a special endpoint that can be only accessible to a user who previously logged in. +We will learn how to implement user registration and securely store user passwords. We will also learn how to implement a login endpoint and check if the provided username / password combination is correct. Lastly, we will implement a special endpoint that can be only accessible to a user who previously logged in. Lastly, it is time to learn how to automate the testing of our API's. This can be done in Postman using [automated testsuites](https://www.postman.com/use-cases/api-testing-automation/) but we are going to do it using code, similar to unit testing learned in JavaScript. Have a look [here](https://study.hackyourfuture.net/#/testing/api-testing.md) on how to do that using the [supertest](https://www.npmjs.com/package/supertest) library. From 58bb9955dfea1d45dd55456bd28dc150c4daa11e Mon Sep 17 00:00:00 2001 From: "U. K" <43114134+allhandsondeck@users.noreply.github.com> Date: Fri, 9 Feb 2024 14:53:14 +0100 Subject: [PATCH 24/31] Update LESSONPLAN.md --- week3/LESSONPLAN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/week3/LESSONPLAN.md b/week3/LESSONPLAN.md index 2169630ee..e3ec62c16 100644 --- a/week3/LESSONPLAN.md +++ b/week3/LESSONPLAN.md @@ -2,7 +2,7 @@ ## Agenda -1. Authentication +1. Authentication & Authorisation 2. User registration and using bcrypt to store passwords. 3. Login, protected endpoints and logout 4. Testing From ad2769f28fdecfade6b00dece7e6b15c7e895f43 Mon Sep 17 00:00:00 2001 From: allhandsondeck Date: Sat, 10 Feb 2024 14:31:12 +0100 Subject: [PATCH 25/31] doc: add prep exercise of week 3 --- week3/build-with-students/app.js | 16 --- week3/build-with-students/users.js | 134 ------------------ week3/prep-exercise/README.md | 32 +++++ week3/prep-exercise/app.js | 12 ++ .../package.json | 4 +- week3/prep-exercise/users.js | 4 + 6 files changed, 49 insertions(+), 153 deletions(-) delete mode 100644 week3/build-with-students/app.js delete mode 100644 week3/build-with-students/users.js create mode 100644 week3/prep-exercise/README.md create mode 100644 week3/prep-exercise/app.js rename week3/{build-with-students => prep-exercise}/package.json (86%) create mode 100644 week3/prep-exercise/users.js diff --git a/week3/build-with-students/app.js b/week3/build-with-students/app.js deleted file mode 100644 index cd01d6424..000000000 --- a/week3/build-with-students/app.js +++ /dev/null @@ -1,16 +0,0 @@ -import express from 'express'; -import { register, login, getProfile, logout } from './users.js'; - -let app = express(); - -app.use(express.json()); - -app.post ( "/register", register); -app.post ( "/login", login); -app.post ( "/logout", logout); -app.get ( "/profile", getProfile); - - -app.listen(3000, () => { - console.log('Server is running on port 3000'); -}); diff --git a/week3/build-with-students/users.js b/week3/build-with-students/users.js deleted file mode 100644 index b591591a9..000000000 --- a/week3/build-with-students/users.js +++ /dev/null @@ -1,134 +0,0 @@ -import { v4 as generateUUID } from 'uuid'; -import { hash, compare } from 'bcrypt'; - -// The higher the number, the more secure password but also slower the hashing process. -const saltRounds = 10; - -const usersDatabase = [{ - id: "9a2cd641-3bc5-4334-9813-926543e08426", - username: "alice1", - password: "$2b$10$Coxs6k4gAnMIKI1wcgpRpOVHSD7um5QaIoHBCqfeLHu6yYRqyRbcm" // 1234 -}]; - -let sessions = [ - { - token: "13422ece-d321-49d8-8f8f-11cb6d67287f", - userId: "9a2cd641-3bc5-4334-9813-926543e08426" - } -]; - -export const register = async (req, res) => { - // Check request body - if (!req.body.username || !req.body.password) { - res.status(400).json({ message: 'Please provide username and password' }).end(); - return; - } - - // Check if username already exists - const isUsernameExists = getUserByUsername(req.body.username) !== undefined; - if (isUsernameExists) { - res.status(400).json({ message: 'Username already exists' }).end(); - return; - } - - // Hash the password and create new user - const hashedPassword = await hash(req.body.password, saltRounds); - const newUser = { - id: generateUUID(), - username: req.body.username, - password: hashedPassword, - }; - - // Save user to usersDatabase - usersDatabase.push(newUser); - - // Return success and the new user to the client - res.status(201).json({ - id: newUser.id, - username: newUser.username, - }).end(); -}; - - -export const login = async (req, res) => { - // Check request body - if (!req.body.username || !req.body.password) { - res.status(400).json({ message: 'Please provide username and password' }).end(); - return; - } - - // Find user in the database - const user = getUserByUsername(req.body.username); - if (!user) { - res.status(401).json({ message: 'Invalid username / password combination' }).end(); - return; - } - - // Check if password is correct by using bcrypt compare - const isPasswordCorrect = await compare(req.body.password, user.password); - if (!isPasswordCorrect) { - res.status(401).json({ message: 'Invalid username / password combination' }).end(); - return; - } - - // Login successfully - create a session token - const token = generateUUID(); - - // Save the session token - sessions.push({ token, userId: user.id }); - - // Return the token to the client - // The client should save the token and send it in the Authorization header for future requests: - // Authorization: bearer - res.status(200).json({ token }).end(); -}; - - -export const getProfile = (req, res) => { - // Check if user is logged in - const token = extractBearerTokenFromAuth(req.headers.authorization); - const session = getSessionByToken(token); - if (!session) { - res.status(401).json({ message: 'You are not logged in' }).end(); - return; - } - - // Get user details from the session - const user = getUserById(session.userId); - if (!user) { - res.status(401).json({ message: 'You are not logged in' }).end(); - return; - } - - // Return a message with the username - res.status(200).json({ message: `Hello! You are currently logged in as ${user.username}!` }).end(); -}; - -export const logout = (req, res) => { - const token = extractBearerTokenFromAuth(req.headers.authorization); - - // remove the session from the sessions array - sessions = sessions.filter(session => session.token !== token); - res.status(204).end(); -}; - - -// Helper functions -const getUserByUsername = (username) => { - return usersDatabase.find(user => user.username === username); -}; - -const getUserById = (userID) => { - return usersDatabase.find(user => user.id === userID); -}; - -const getSessionByToken = (token) => { - return sessions.find(session => session.token === token); -}; - -const extractBearerTokenFromAuth = (authorization) => { - if (!authorization || !authorization.startsWith('Bearer ')) { - return null; - } - return authorization.replace('Bearer ', ''); -} \ No newline at end of file diff --git a/week3/prep-exercise/README.md b/week3/prep-exercise/README.md new file mode 100644 index 000000000..ff10bd16d --- /dev/null +++ b/week3/prep-exercise/README.md @@ -0,0 +1,32 @@ +In this exercise, you will build a secure authentication and authorisation system using Node.js and Express.js with four main endpoints: register, login, getProfile, and logout. The system will utilise JWT (JSON Web Tokens) for managing user sessions. + +Requirements: + +1. Register Endpoint: + + - Implement a POST endpoint /register that allows users to register with a username and password. + - Validate the request body to ensure it includes a username and password. + - Hash the user's password using bcrypt before storing it in memory. + - Return a success message along with the user's ID and username upon successful registration. + +1. Login Endpoint: + + - Create a POST endpoint /login that allows users to log in with their registered credentials. + - Validate the request body to ensure it includes a username and password. + - Verify the user's credentials by comparing the hashed password stored in memory. + - If authentication succeeds, generate a JWT containing the user's ID and sign it with a secret key. + - Return the JWT token to the client upon successful login. + +1. Get Profile Endpoint: + + - Implement a GET endpoint /profile that allows authenticated users to retrieve their profile information. + - Extract the JWT token from the Authorization header. + - Verify the JWT token and decode the payload to retrieve the user's ID. + - Retrieve the user's profile information from memory using the decoded user ID. + - Return a message with the user's username upon successful profile retrieval. + +1. Logout Endpoint: + + - Create a POST endpoint /logout that allows users to logout and invalidate their JWT token. + - No server-side token invalidation is required; the client should handle token deletion. + - Return a success response with a status code indicating successful logout (e.g., 204 No Content). diff --git a/week3/prep-exercise/app.js b/week3/prep-exercise/app.js new file mode 100644 index 000000000..8c8bdb12c --- /dev/null +++ b/week3/prep-exercise/app.js @@ -0,0 +1,12 @@ +import express from "express"; +// Use below import statement for importing middlewares from users.js for your routes +// import { ....... } from "./users.js"; + +let app = express(); + +app.use(express.json()); +// Create routes here, e.g. app.post("/register", .......) + +app.listen(3000, () => { + console.log("Server is running on port 3000"); +}); diff --git a/week3/build-with-students/package.json b/week3/prep-exercise/package.json similarity index 86% rename from week3/build-with-students/package.json rename to week3/prep-exercise/package.json index a6891a78d..8e6544dd5 100644 --- a/week3/build-with-students/package.json +++ b/week3/prep-exercise/package.json @@ -11,8 +11,6 @@ "author": "", "license": "ISC", "dependencies": { - "bcrypt": "^5.1.1", "express": "^4.18.2", - "uuid": "^9.0.1" } -} +} \ No newline at end of file diff --git a/week3/prep-exercise/users.js b/week3/prep-exercise/users.js new file mode 100644 index 000000000..679281f0c --- /dev/null +++ b/week3/prep-exercise/users.js @@ -0,0 +1,4 @@ +// Create middlewares required for routes defined in app.js +// export const register = async (req, res) => {}; + +// You can also create helper functions in this file to help you implement logic inside middlewares From 17d5d00f697e0ef6dec13cffb00fd43863febc14 Mon Sep 17 00:00:00 2001 From: stasel <2033301+stasel@users.noreply.github.com> Date: Wed, 28 Feb 2024 14:12:06 +0100 Subject: [PATCH 26/31] Changed class -> cohort --- hand-in-assignments-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hand-in-assignments-guide.md b/hand-in-assignments-guide.md index 8fbd06002..bb46f92ae 100644 --- a/hand-in-assignments-guide.md +++ b/hand-in-assignments-guide.md @@ -12,7 +12,7 @@ Watch the video (by clicking the image) or go through the following walk-through ONE TIME ONLY (START OF EVERY MODULE) -1. Create a [fork](https://help.github.com/en/articles/fork-a-repo) of the assignments module repository. For Node.js, the assignments module repository is `https://www.github.com/HackYourAssignment/Node.js-classXX` where XX is your class number. You do this by using the `fork` option on the top right +1. Create a [fork](https://help.github.com/en/articles/fork-a-repo) of the assignments module repository. For Node.js, the assignments module repository is `https://www.github.com/HackYourAssignment/Node.js-cohortXX` where XX is your class number. You do this by using the `fork` option on the top right 2. Navigate to the URL of the cloned repository (it should be in your personal GitHub account, under "repositories") 3. Clone the repository, using SSH, to your local machine. You can do this by typing in `git clone ` in the command line 4. On your local machine, navigate to the folder using the command line From ba4a33b0c449f2005ceb675d7cc1b090a768dac9 Mon Sep 17 00:00:00 2001 From: Jim Cramer Date: Sun, 12 May 2024 14:58:47 +0200 Subject: [PATCH 27/31] Add client for week 3 JWT prep exercise (#636) Add a client for week3 prep exercise --- .gitignore | 5 +- .vscode/launch.json | 18 ++- .vscode/settings.json | 5 +- week3/prep-exercise/README.md | 42 +++++-- week3/prep-exercise/app.js | 12 -- .../assets/client-state-diagram.drawio | 114 ++++++++++++++++++ .../assets/client-state-diagram.png | Bin 0 -> 30462 bytes week3/prep-exercise/client/index.html | 36 ++++++ week3/prep-exercise/client/public/hyf.png | Bin 0 -> 2905 bytes week3/prep-exercise/client/public/style.css | 25 ++++ week3/prep-exercise/client/src/app.js | 21 ++++ .../prep-exercise/client/src/initialState.js | 3 + .../client/src/pages/homePage.js | 71 +++++++++++ .../client/src/pages/loginPage.js | 50 ++++++++ .../client/src/pages/registerPage.js | 48 ++++++++ .../client/src/pages/registerSuccessPage.js | 16 +++ .../client/src/util/fetchAndLog.js | 19 +++ .../client/src/util/getViewIds.js | 15 +++ .../client/src/util/initializeState.js | 19 +++ .../prep-exercise/client/src/util/loadPage.js | 18 +++ week3/prep-exercise/client/src/util/logger.js | 79 ++++++++++++ .../client/src/util/tokenUtils.js | 13 ++ .../client/src/views/homeView.js | 53 ++++++++ .../client/src/views/loginView.js | 59 +++++++++ .../client/src/views/modalDialogView.js | 32 +++++ .../client/src/views/registerSuccessView.js | 48 ++++++++ .../client/src/views/registerView.js | 57 +++++++++ week3/prep-exercise/package.json | 12 +- week3/prep-exercise/server/app.js | 15 +++ week3/prep-exercise/{ => server}/users.js | 3 +- 30 files changed, 877 insertions(+), 31 deletions(-) delete mode 100644 week3/prep-exercise/app.js create mode 100644 week3/prep-exercise/assets/client-state-diagram.drawio create mode 100644 week3/prep-exercise/assets/client-state-diagram.png create mode 100644 week3/prep-exercise/client/index.html create mode 100644 week3/prep-exercise/client/public/hyf.png create mode 100644 week3/prep-exercise/client/public/style.css create mode 100644 week3/prep-exercise/client/src/app.js create mode 100644 week3/prep-exercise/client/src/initialState.js create mode 100644 week3/prep-exercise/client/src/pages/homePage.js create mode 100644 week3/prep-exercise/client/src/pages/loginPage.js create mode 100644 week3/prep-exercise/client/src/pages/registerPage.js create mode 100644 week3/prep-exercise/client/src/pages/registerSuccessPage.js create mode 100644 week3/prep-exercise/client/src/util/fetchAndLog.js create mode 100644 week3/prep-exercise/client/src/util/getViewIds.js create mode 100644 week3/prep-exercise/client/src/util/initializeState.js create mode 100644 week3/prep-exercise/client/src/util/loadPage.js create mode 100644 week3/prep-exercise/client/src/util/logger.js create mode 100644 week3/prep-exercise/client/src/util/tokenUtils.js create mode 100644 week3/prep-exercise/client/src/views/homeView.js create mode 100644 week3/prep-exercise/client/src/views/loginView.js create mode 100644 week3/prep-exercise/client/src/views/modalDialogView.js create mode 100644 week3/prep-exercise/client/src/views/registerSuccessView.js create mode 100644 week3/prep-exercise/client/src/views/registerView.js create mode 100644 week3/prep-exercise/server/app.js rename week3/prep-exercise/{ => server}/users.js (81%) diff --git a/.gitignore b/.gitignore index b0c0b114e..b6b402ed9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,7 @@ assignments-solution node_modules npm-debug.log package-lock.json -yarn-error.log \ No newline at end of file +yarn-error.log +*.bkp + +week3/prep-exercise/server-demo/ diff --git a/.vscode/launch.json b/.vscode/launch.json index 1348a90a2..6a1000df1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -22,9 +22,7 @@ "--serial", "${workspaceRoot}/week1/assignments/test/server.test.js" ], - "skipFiles": [ - "/**/*.js" - ] + "skipFiles": ["/**/*.js"] }, { "type": "node", @@ -46,6 +44,20 @@ "name": "Launch Week 3 - Lecture", "program": "${workspaceFolder}/week3/lecture/src/index.js", "cwd": "${workspaceFolder}/week3/lecture" + }, + { + "type": "node", + "request": "launch", + "name": "Launch Week 3 - Prep exercise", + "program": "${workspaceFolder}/week3/prep-exercise/server/app.js", + "cwd": "${workspaceFolder}/week3//prep-exercise" + }, + { + "type": "node", + "request": "launch", + "name": "Launch Week 3 - Prep exercise demo", + "program": "${workspaceFolder}/week3/prep-exercise/server-demo/app.js", + "cwd": "${workspaceFolder}/week3//prep-exercise" } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index e1c3e7296..4fec15ae8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,7 @@ { "editor.tabSize": 2, - "eslint.autoFixOnSave": true, "javascript.validate.enable": false, "editor.codeActionsOnSave": { - "source.fixAll.eslint": true - }, + "source.fixAll.eslint": "explicit" + } } diff --git a/week3/prep-exercise/README.md b/week3/prep-exercise/README.md index ff10bd16d..4290c3ea1 100644 --- a/week3/prep-exercise/README.md +++ b/week3/prep-exercise/README.md @@ -1,32 +1,56 @@ -In this exercise, you will build a secure authentication and authorisation system using Node.js and Express.js with four main endpoints: register, login, getProfile, and logout. The system will utilise JWT (JSON Web Tokens) for managing user sessions. +# Prep exercise + +## Server + +In this exercise, you will build a secure authentication and authorization system using Node.js and Express.js with four main endpoints: `register`, `login`, `getProfile`, and `logout`. The system will utilize JWT (JSON Web Tokens) for managing user sessions. + +Files to be modified are located in the `server` folder. Requirements: 1. Register Endpoint: - - Implement a POST endpoint /register that allows users to register with a username and password. + - Implement a `POST` endpoint `/auth/register` that allows users to register with a username and password. - Validate the request body to ensure it includes a username and password. - - Hash the user's password using bcrypt before storing it in memory. - - Return a success message along with the user's ID and username upon successful registration. + - Hash the user's password using `bcrypt` before storing it in memory. + - Return a success message along with the user's ID and username upon successful registration, format: `{id: , username: }` + - In case of any errors along the way, return an appropriate error message (format: `{message: }`) with a corresponding status code in the 40x range. 1. Login Endpoint: - - Create a POST endpoint /login that allows users to log in with their registered credentials. - - Validate the request body to ensure it includes a username and password. + - Create a `POST` endpoint `/auth/login` that allows users to log in with their registered credentials. - Verify the user's credentials by comparing the hashed password stored in memory. - If authentication succeeds, generate a JWT containing the user's ID and sign it with a secret key. - - Return the JWT token to the client upon successful login. + - Return the JWT token to the client upon successful login, format: `{token: }` with status code 201. + - In case of any errors along the way, return an appropriate error message (format: `{message: }`) with a corresponding status code in the 40x range. 1. Get Profile Endpoint: - - Implement a GET endpoint /profile that allows authenticated users to retrieve their profile information. + - Implement a `GET` endpoint `/auth/profile` that allows authenticated users to retrieve their profile information. - Extract the JWT token from the Authorization header. - Verify the JWT token and decode the payload to retrieve the user's ID. - Retrieve the user's profile information from memory using the decoded user ID. - Return a message with the user's username upon successful profile retrieval. + - In case of any errors along the way, return an appropriate error message (format: `{message: }`) with a status code 401 (Unauthorized). 1. Logout Endpoint: - - Create a POST endpoint /logout that allows users to logout and invalidate their JWT token. + - Create a `POST` endpoint `/auth/logout` that allows users to logout and invalidate their JWT token. - No server-side token invalidation is required; the client should handle token deletion. - Return a success response with a status code indicating successful logout (e.g., 204 No Content). + +## Client (optional) + +While you can test the endpoints of your API with Postman and/or by creating unit tests with Jest and Supertest, we have also provided a fully functional demo front-end application that demonstrates how a web token based authentication system might be used from the front-end side. The demo front-end resides in the `client` folder and is statically served by the backend. The client expects an API that meets the specification as outlined above. + +The client allows you to register, login and logout. After logging in, it uses the received JWT token to fetch the profile of the logged-in user and shows it on its home page. If this fetch fails, e.g. due to an expired token, the user is redirected to the login page. + +Upon logging in, the client stores the JWT token in `localStorage`. When the client starts it tries to load this token from `localStorage`. If a token was found it try to load the client's home page directly. This may fail if the token is expired as mentioned earlier in which case the login page is loaded. If no token was found in `localStorage` at client startup the login page is loaded directly. + +When logging out, the token is removed from `localStorage` and the user is redirected to the login page. + +The process is illustrated in the diagram below. + +![client-date-diagram](./assets/client-state-diagram.png) + +The client code logs debug information in the browser console. This may help you to follow the application flow as you navigate through its pages. diff --git a/week3/prep-exercise/app.js b/week3/prep-exercise/app.js deleted file mode 100644 index 8c8bdb12c..000000000 --- a/week3/prep-exercise/app.js +++ /dev/null @@ -1,12 +0,0 @@ -import express from "express"; -// Use below import statement for importing middlewares from users.js for your routes -// import { ....... } from "./users.js"; - -let app = express(); - -app.use(express.json()); -// Create routes here, e.g. app.post("/register", .......) - -app.listen(3000, () => { - console.log("Server is running on port 3000"); -}); diff --git a/week3/prep-exercise/assets/client-state-diagram.drawio b/week3/prep-exercise/assets/client-state-diagram.drawio new file mode 100644 index 000000000..6d4d7dbcb --- /dev/null +++ b/week3/prep-exercise/assets/client-state-diagram.drawio @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/week3/prep-exercise/assets/client-state-diagram.png b/week3/prep-exercise/assets/client-state-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..8788af4c5a1993ad7023ddfb8929e61ba8373c1e GIT binary patch literal 30462 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 rq0y|m>irF-3Jal&l|pf-2(Q=X_)reO5WEZ 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 DOiO|} literal 0 HcmV?d00001 diff --git a/week3/prep-exercise/client/index.html b/week3/prep-exercise/client/index.html new file mode 100644 index 000000000..6194498c8 --- /dev/null +++ b/week3/prep-exercise/client/index.html @@ -0,0 +1,36 @@ + + + + + + + + + + + + Codestin Search App + + + + + + + + + + + +
    + + + diff --git a/week3/prep-exercise/client/public/hyf.png b/week3/prep-exercise/client/public/hyf.png new file mode 100644 index 0000000000000000000000000000000000000000..f009198ac0916c672063c2b427f98d839884d3b5 GIT binary patch literal 2905 zcmZ`*cTm$^7X1N1klwq5-b*L~2_1v<7C=yX?>#D@U}%PFL_`n>NJ5d1CK44Ppa`M} zA^~Yi5eWjJNeiF?%kIwZ% zaPs$f)LC0_T46w1*qAabF@f2nq)Zg37yw{`TELB+;=inyxHY&t3%+_{ zkg!UNvb1|*qA{eqw|Co@s3!o{)Dti_he-KypFco3aEI9^kU&Yp>9PxS3dU(1x%?80 zRKk5OGLHU&3EZe^-JLocrVOSA1llf@wFX_`@}(Lo$_7!_jvAwmCT=CIy3L6OlW?Xv ze9Bh{|9zQld`~q)c z{D2skoY}V{-_3bTolo21q=a8+llY}-gf8S40@jQTc5CRuPrF@Wq=~vzoCqoky@-X_V&k;RsOZj)$H52EBs-Y zeqYG8C5@PoSpHNFbg=W}w}-WXbhT1upZcVJrU7puc%nwVOHDK!d^fFR*@Lz%tg~CJ z8x=WSf}2!NOrf@y50W$d%LOi|>1D0yP1ecU2X4Oshj(L4Il&m#z;nVh#(zTH(E{@s z*V>PRiE36g)Wh%#G{}7_VPZ0tWvkgYrC7UVbornn=3~uM1@SBw1#A$bYP}I_$-5sI)|QI zE)4E9pH^{d@iNz&6(!KjQ^+1hwpI8wx63PaaQuw&xIttki#FeYbJxa3I5!Py%5l+_ zHsz<8`_z%%^D&0|J%#hE)7h<)^WZaEX)zDKF0u>>r+cw_U*Tagy5+%M;3FtWzjYLI zGG@@sC+e(0h#I2T^tgJK(zTy zxq!*9cfU#O3pmx-i}P{^7lAQE5o0VtbK~X2{JlomG+KFP*p{F+Q z&#IKos+U_jEhs&CE}2iNz*}6ts7;qB0YX`!Nf{oUNyToc~$ zz=aErp?S~YtJYx)#?2<;1Bw#p`ua25#e_+yL@R(c$y(Y0v(>&~oetaoY^Ec@qEfTn zM?)ZkqI6XzU`%&pblKvvp!GHaVb`%GGc`^F>u6t0gT~)mx);+_NpsJwcINkYMBH2` zyPfc^LOjT1!?EB6*=u0I%@3?~hm$tnxTuUc*wAed5Etv@`~%^y#r%q+pP{NJzhGtX zrXd8ZeH>X-P>=?%BsZ!{Dq^HEOe*-eR;m=8AnkDia{?XjNy|i<@%nv*owK92aIZXl zOeFC5g|QoaS7PEjBSu5F>6ylk{9atjBgF2wXfdnyp+BQL9Oo^7;*ZXZam? zGR=7tJdw8Sb|~4yEf}xRS&oPtmn~(Y>m!>H@GnThr(E$u>4u73ieDu3x$o9otEOc0 z>B`#f@Tb`8VhTpA^sGLq)+q$82*Py+#{zqwxc8TAsJ4GsnO8xQ^DaH&X#3320zKSu zd^r^T=-c_H6B;@Z#xOmDSNiOR;5&nliBl=!1fOKFor5uqd0E`A$Mcef-n>i6`_sEc zg8Z6It-_mj31|;V3a{E>sk5L2o$B{beILW^>aT}Y5rsajZx%^~QrTQYnl@a8K1H_- zh%A465b2=*d)jHQAS64~R01M^pnTljh2W5{f} zrHsswUsQ+yXjy##QoLJEIKEa`fHJ7jZ1+5W+~X;+6pgSmRZM8N<65}}lUr3|D%3Bf zt@0M1dpw0Gn{s~mj-MBnGK?NK5K6d9HeeTR+*U-trFbOPVBT6}^DL;qiY4rFr9%gw>V(a{w}Tw@zS1L7@Lod+hLMIt1-&@W z3taDwQc}$7IYj>z!{_rI?^jyOU5N?Ft1Ciuh4Y5CXwQto^-a9jAI(ld+Keb3?tos=S{gr8$^+Vr4U* z1S69Js(msp4g}RZMTtt_7KayBDXoUn2MG*!&sh$H;{+(>*7DCT6@L?CsY#YC@yu^} zn08(Q7q3`d_^M&D2>2tMyxZ)s?iF|X3Dx;PmO@tWn~O2aVnudNs@eKlANH=chPKj& zoeagtt!e0z67yXu9pTaPb(RUtvmnNDmLCK1DIytE{u`*1*JsH_lS^wN;g%w{29JPQ z)I^KxVdZ(Yb6h!Nw@5(L8@DR;y`u6h;n%I(7t*fN#zCn~=S*`n(skA={+BcRmsk5~ zsPu { + state = { ...state, ...updates }; + logger.debug('state', state); + view.update(state); + }; + + const onLogout = async () => { + removeToken(); + + // reset state + state = initializeState(); + + try { + const response = await fetchAndLog('/auth/logout', { + method: 'POST', + }); + + if (!response.ok) { + throw new Error(`Logout failed. Reason: HTTP ${response.status}`); + } + + loadPage(createLoginPage, state); + } catch (error) { + state = { ...state, error: error.message }; + updateView(state); + } + }; + + const getProfile = async () => { + try { + const response = await fetchAndLog('/auth/profile', { + method: 'GET', + headers: { + Authorization: `Bearer ${state.token}`, + }, + }); + + const data = await response.json(); + + if (!response.ok) { + state = { ...state, error: data.message }; + logger.debug('state', state); + removeToken(); + state = initializeState(); + loadPage(createLoginPage, state); + return; + } + + updateView({ profile: data.message }); + } catch (error) { + state = { ...state, error: error.message }; + updateView(state); + } + }; + + const view = createHomeView({ onLogout }); + getProfile(); + + return view; +} + +export default createHomePage; diff --git a/week3/prep-exercise/client/src/pages/loginPage.js b/week3/prep-exercise/client/src/pages/loginPage.js new file mode 100644 index 000000000..42c93b0c0 --- /dev/null +++ b/week3/prep-exercise/client/src/pages/loginPage.js @@ -0,0 +1,50 @@ +import loadPage from '../util/loadPage.js'; +import logger from '../util/logger.js'; +import { putToken } from '../util/tokenUtils.js'; +import fetchAndLog from '../util/fetchAndLog.js'; +import createLoginView from '../views/loginView.js'; +import createRegisterPage from './registerPage.js'; +import createHomePage from './homePage.js'; + +function createLoginPage(state) { + const updateView = (updates) => { + state = { ...state, ...updates }; + logger.debug('state', state); + view.update(state); + }; + + const onSubmit = async (username, password) => { + try { + const response = await fetchAndLog('/auth/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ username, password }), + }); + + const data = await response.json(); + if (!response.ok) { + throw new Error(data.message); + } + + putToken(data.token); + state = { ...state, token: data.token, error: null }; + + loadPage(createHomePage, state); + } catch (error) { + state = { ...state, error: error.message }; + updateView(state); + } + }; + + const onRegister = () => { + loadPage(createRegisterPage, state); + }; + + const view = createLoginView({ onSubmit, onRegister }); + + return view; +} + +export default createLoginPage; diff --git a/week3/prep-exercise/client/src/pages/registerPage.js b/week3/prep-exercise/client/src/pages/registerPage.js new file mode 100644 index 000000000..4ea4f9ec6 --- /dev/null +++ b/week3/prep-exercise/client/src/pages/registerPage.js @@ -0,0 +1,48 @@ +import loadPage from '../util/loadPage.js'; +import logger from '../util/logger.js'; +import fetchAndLog from '../util/fetchAndLog.js'; +import createRegisterView from '../views/registerView.js'; +import createRegisterSuccessPage from './registerSuccessPage.js'; +import createLoginPage from './loginPage.js'; + +function createRegisterPage(state) { + const updateView = (updates) => { + state = { ...state, ...updates }; + logger.debug('state', state); + view.update(state); + }; + + const onSubmit = async (username, password) => { + try { + const response = await fetchAndLog('/auth/register', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ username, password }), + }); + + const data = await response.json(); + if (!response.ok) { + throw new Error(data.message); + } + + logger.debug('response', data); + + loadPage(createRegisterSuccessPage, state); + } catch (error) { + state = { ...state, error: error.message }; + updateView(state); + } + }; + + const onLogin = () => { + loadPage(createLoginPage, state); + }; + + const view = createRegisterView({ onSubmit, onLogin }); + + return view; +} + +export default createRegisterPage; diff --git a/week3/prep-exercise/client/src/pages/registerSuccessPage.js b/week3/prep-exercise/client/src/pages/registerSuccessPage.js new file mode 100644 index 000000000..1980cf39b --- /dev/null +++ b/week3/prep-exercise/client/src/pages/registerSuccessPage.js @@ -0,0 +1,16 @@ +import loadPage from '../util/loadPage.js'; +import logger from '../util/logger.js'; +import createRegisterSuccessView from '../views/registerSuccessView.js'; +import createLoginPage from './loginPage.js'; + +function createRegisterSuccessPage(state) { + const onLogin = () => { + loadPage(createLoginPage, state); + }; + + const view = createRegisterSuccessView({ onLogin }); + + return view; +} + +export default createRegisterSuccessPage; diff --git a/week3/prep-exercise/client/src/util/fetchAndLog.js b/week3/prep-exercise/client/src/util/fetchAndLog.js new file mode 100644 index 000000000..c10a7c796 --- /dev/null +++ b/week3/prep-exercise/client/src/util/fetchAndLog.js @@ -0,0 +1,19 @@ +import logger from './logger.js'; + +/** + * A wrapper function for `fetch` with before and after logging + * @param {*} url Same as for fetch + * @param {*} options Same as for fetch + * @returns Same as for fetch + */ +async function fetchAndLog(url, options = { method: 'GET' }) { + const { method, ...rest } = options; + logger.debug('fetch', `${method} ${url}`, ...Object.values(rest)); + + const response = await fetch(url, options); + + logger.debug('status', response.status); + return response; +} + +export default fetchAndLog; diff --git a/week3/prep-exercise/client/src/util/getViewIds.js b/week3/prep-exercise/client/src/util/getViewIds.js new file mode 100644 index 000000000..1239de048 --- /dev/null +++ b/week3/prep-exercise/client/src/util/getViewIds.js @@ -0,0 +1,15 @@ +/** + * Get all child elements with an `id` attribute, starting from `root`. + * @param {HTMLElement} root The root element to start from. + * @returns An object with `id` as key and an element reference as value. + */ +function getViewIds(root) { + const elementsWithIds = Array.from(root.querySelectorAll('[id]')); + const dom = {}; + for (const elem of elementsWithIds) { + dom[elem.id] = elem; + } + return dom; +} + +export default getViewIds; diff --git a/week3/prep-exercise/client/src/util/initializeState.js b/week3/prep-exercise/client/src/util/initializeState.js new file mode 100644 index 000000000..8bc3d80a0 --- /dev/null +++ b/week3/prep-exercise/client/src/util/initializeState.js @@ -0,0 +1,19 @@ +import initialState from '../initialState.js'; +import { getToken } from './tokenUtils.js'; + +/** + * Initialize the state, including token if present from localStorage. + * @returns A an initialized state object + */ +function initializeState() { + const state = { ...initialState }; + + const token = getToken(); + if (token) { + state.token = token; + } + + return state; +} + +export default initializeState; diff --git a/week3/prep-exercise/client/src/util/loadPage.js b/week3/prep-exercise/client/src/util/loadPage.js new file mode 100644 index 000000000..52a04838a --- /dev/null +++ b/week3/prep-exercise/client/src/util/loadPage.js @@ -0,0 +1,18 @@ +import logger from './logger.js'; + +/** + * Load an application page + * @param {*} pageFactoryFn Factory function for the page to load + * @param {*} state State object to be passed to the page + */ +function loadPage(pageFactoryFn, state) { + logger.debug('loadPage', pageFactoryFn.name.replace('create', '')); + logger.debug('state', state); + const appRoot = document.getElementById('app-root'); + appRoot.innerHTML = ''; + + const page = pageFactoryFn(state); + appRoot.appendChild(page.root); +} + +export default loadPage; diff --git a/week3/prep-exercise/client/src/util/logger.js b/week3/prep-exercise/client/src/util/logger.js new file mode 100644 index 000000000..7da6b5c93 --- /dev/null +++ b/week3/prep-exercise/client/src/util/logger.js @@ -0,0 +1,79 @@ +/** + * This file is provided ready-made for use in your application by HackYourFuture. + * There should be no reason to make any changes to this file. + */ + +// Log levels in increasing severity +const LEVELS = ['silly', 'debug', 'info', 'warn', 'error', 'fatal', 'none']; + +/** + * Create a logger object. + * @returns + */ +function logger() { + let minLevel = LEVELS.length - 1; + + // Check the requested level against the minimum level + const isMinLevel = (level) => LEVELS.indexOf(level) >= minLevel; + + // The function that does the actual logging. + const log = (level, label, ...args) => { + if (!isMinLevel(level)) { + return; + } + + let logFn; + + switch (level) { + case 'warn': + logFn = console.warn; + break; + case 'info': + logFn = console.info; + break; + case 'error': + logFn = console.error; + break; + default: + logFn = console.log; + } + + logFn(`${level}: ${label} =>`, ...args); + }; + + // Return an object with convenience functions for logging at specific + // log levels. + return { + setLevel(level) { + const newLevel = LEVELS.indexOf(level); + if (newLevel !== -1) { + minLevel = newLevel; + } + }, + getLevel() { + return LEVELS[minLevel]; + }, + isMinLevel, + log, + silly(label, ...args) { + log('silly', label, ...args); + }, + debug(label, ...args) { + log('debug', label, ...args); + }, + info(label, ...args) { + log('info', label, ...args); + }, + warn(label, ...args) { + log('warn', label, ...args); + }, + error(label, ...args) { + log('error', label, ...args); + }, + fatal(label, ...args) { + log('fatal', label, ...args); + }, + }; +} + +export default logger(); diff --git a/week3/prep-exercise/client/src/util/tokenUtils.js b/week3/prep-exercise/client/src/util/tokenUtils.js new file mode 100644 index 000000000..8d0c0788e --- /dev/null +++ b/week3/prep-exercise/client/src/util/tokenUtils.js @@ -0,0 +1,13 @@ +const TOKEN_NAME = 'token'; + +export function getToken() { + return localStorage.getItem(TOKEN_NAME); +} + +export function putToken(token) { + localStorage.setItem(TOKEN_NAME, token); +} + +export function removeToken() { + localStorage.removeItem(TOKEN_NAME); +} diff --git a/week3/prep-exercise/client/src/views/homeView.js b/week3/prep-exercise/client/src/views/homeView.js new file mode 100644 index 000000000..2053e45c2 --- /dev/null +++ b/week3/prep-exercise/client/src/views/homeView.js @@ -0,0 +1,53 @@ +import getViewIds from '../util/getViewIds.js'; +import createModalDialView from './modalDialogView.js'; + +function createHomeView(props) { + const root = document.createElement('div'); + root.innerHTML = String.raw` +
    +
    + + +

    +
    +
    + `; + + const modalView = createModalDialView({ title: 'Error' }); + root.append(modalView.root); + + const dom = getViewIds(root); + + const sideNavElements = root.querySelectorAll('.sidenav'); + const sideNavInstances = M.Sidenav.init(sideNavElements); + + const logoutHandler = (event) => { + event.preventDefault(); + sideNavInstances[0].close(); + props.onLogout(); + }; + + dom.logoutBtn.addEventListener('click', logoutHandler); + dom.mobileLogoutBtn.addEventListener('click', logoutHandler); + + const update = (state) => { + dom.profile.textContent = state.profile; + modalView.update(state); + }; + + return { root, update }; +} + +export default createHomeView; diff --git a/week3/prep-exercise/client/src/views/loginView.js b/week3/prep-exercise/client/src/views/loginView.js new file mode 100644 index 000000000..1575906d6 --- /dev/null +++ b/week3/prep-exercise/client/src/views/loginView.js @@ -0,0 +1,59 @@ +import getViewIds from '../util/getViewIds.js'; +import createModalDialView from './modalDialogView.js'; + +const MESSAGE_TIMEOUT_MS = 2000; + +function createLoginView(props) { + const root = document.createElement('div'); + root.innerHTML = String.raw` +
    +
    +
    +
    Login
    +
    +
    + + +
    +
    + + +
    + +
    +
    +

    Not yet registered? + + Create an account + +

    +
    +
    +
    +
    + `; + + const modalView = createModalDialView({ title: 'Login Failed' }); + root.append(modalView.root); + + const dom = getViewIds(root); + + dom.form.addEventListener('submit', (event) => { + event.preventDefault(); + props.onSubmit(dom.username.value, dom.password.value); + }); + + dom.registerLink.addEventListener('click', (event) => { + event.preventDefault(); + props.onRegister(); + }); + + const update = (state) => { + modalView.update(state); + }; + + return { root, update }; +} + +export default createLoginView; diff --git a/week3/prep-exercise/client/src/views/modalDialogView.js b/week3/prep-exercise/client/src/views/modalDialogView.js new file mode 100644 index 000000000..96d0dbdcb --- /dev/null +++ b/week3/prep-exercise/client/src/views/modalDialogView.js @@ -0,0 +1,32 @@ +function createModalDialView(props) { + const root = document.createElement('div'); + root.innerHTML = String.raw` + + + `; + + const dom = {}; + dom.modalText = root.querySelector('#modal-text'); + + const modalElements = root.querySelectorAll('.modal'); + const modalInstances = M.Modal.init(modalElements); + + const update = (state) => { + if (state.error) { + dom.modalText.textContent = state.error; + modalInstances[0].open(); + } + }; + + return { root, update }; +} + +export default createModalDialView; diff --git a/week3/prep-exercise/client/src/views/registerSuccessView.js b/week3/prep-exercise/client/src/views/registerSuccessView.js new file mode 100644 index 000000000..b5ccb4843 --- /dev/null +++ b/week3/prep-exercise/client/src/views/registerSuccessView.js @@ -0,0 +1,48 @@ +import getViewIds from '../util/getViewIds.js'; + +function createRegisterSuccessView(props) { + const root = document.createElement('div'); + root.innerHTML = String.raw` +
    +
    + + +
    + +
    +
    Registration was successful!
    +

    You can now login with your username and password.

    +
    +
    + `; + + const dom = getViewIds(root); + + const sideNavElements = root.querySelectorAll('.sidenav'); + const sideNavInstances = M.Sidenav.init(sideNavElements); + + const loginHandler = (event) => { + event.preventDefault(); + sideNavInstances[0].close(); + props.onLogin(); + }; + + dom.loginBtn.addEventListener('click', loginHandler); + dom.mobileLoginBtn.addEventListener('click', loginHandler); + + return { root }; +} + +export default createRegisterSuccessView; diff --git a/week3/prep-exercise/client/src/views/registerView.js b/week3/prep-exercise/client/src/views/registerView.js new file mode 100644 index 000000000..8812f1630 --- /dev/null +++ b/week3/prep-exercise/client/src/views/registerView.js @@ -0,0 +1,57 @@ +import getViewIds from '../util/getViewIds.js'; +import createModalDialView from './modalDialogView.js'; + +function createRegisterView(props) { + const root = document.createElement('div'); + root.innerHTML = String.raw` +
    +
    +
    +
    Register
    +
    +
    + + +
    +
    + + +
    + +
    +
    +

    Already registered? + + Login + +

    +
    +
    +
    +
    + `; + + const modalView = createModalDialView({ title: 'Registration Failed' }); + root.append(modalView.root); + + const dom = getViewIds(root); + + dom.registerForm.addEventListener('submit', (event) => { + event.preventDefault(); + props.onSubmit(dom.username.value, dom.password.value); + }); + + dom.loginLink.addEventListener('click', (event) => { + event.preventDefault(); + props.onLogin(); + }); + + const update = (state) => { + modalView.update(state); + }; + + return { root, update }; +} + +export default createRegisterView; diff --git a/week3/prep-exercise/package.json b/week3/prep-exercise/package.json index 8e6544dd5..862e5385b 100644 --- a/week3/prep-exercise/package.json +++ b/week3/prep-exercise/package.json @@ -3,14 +3,22 @@ "version": "1.0.0", "description": "", "main": "app.js", + "types": "module", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "start": "nodemon ./server/app.js", + "demo": "nodemon ./server-demo/app.js" }, "type": "module", "keywords": [], "author": "", "license": "ISC", "dependencies": { + "bcrypt": "^5.1.1", "express": "^4.18.2", + "jsonwebtoken": "^9.0.2", + "uuid": "^9.0.1" + }, + "devDependencies": { + "nodemon": "^3.1.0" } -} \ No newline at end of file +} diff --git a/week3/prep-exercise/server/app.js b/week3/prep-exercise/server/app.js new file mode 100644 index 000000000..a94f09af8 --- /dev/null +++ b/week3/prep-exercise/server/app.js @@ -0,0 +1,15 @@ +import express from 'express'; +// TODO Use below import statement for importing middlewares from users.js for your routes +// TODO import { ....... } from "./users.js"; + +let app = express(); + +app.use(express.json()); +// TODO: Create routes here, e.g. app.post("/register", .......) + +// Serve the front-end application from the `client` folder +app.use(express.static('client')); + +app.listen(3000, () => { + console.log('Server is running on port 3000'); +}); diff --git a/week3/prep-exercise/users.js b/week3/prep-exercise/server/users.js similarity index 81% rename from week3/prep-exercise/users.js rename to week3/prep-exercise/server/users.js index 679281f0c..2cbe3e2cb 100644 --- a/week3/prep-exercise/users.js +++ b/week3/prep-exercise/server/users.js @@ -1,4 +1,5 @@ // Create middlewares required for routes defined in app.js // export const register = async (req, res) => {}; -// You can also create helper functions in this file to help you implement logic inside middlewares +// You can also create helper functions in this file to help you implement logic +// inside middlewares From fd5a7966b3af460e19f1dcd5bb740422c74d5c91 Mon Sep 17 00:00:00 2001 From: Paul Jarleton <5551066+sarlam@users.noreply.github.com> Date: Tue, 25 Jun 2024 15:48:05 +0200 Subject: [PATCH 28/31] feat(week3): Introduce a database helper (#637) Summary ======= We noticed through the course of the previous run that students got overwhelmed by having to implement both the auth layer as well as dealing with persisting them. Since the next module is about database, this PR is a proposal to introduce a `database` helper. ## What's in the box From a student perspective, it adds a bit of reading in the README to understand how to use the database. It also reshapes the readme to have better sections to help navigate it. From our standpoint, it introduces lokijs and uuid in the package.json and a database.js file that allow both to create a persistent or in memory database. --- week3/prep-exercise/README.md | 78 ++++++++++++-- week3/prep-exercise/package.json | 5 +- week3/prep-exercise/server/database.js | 141 +++++++++++++++++++++++++ week3/prep-exercise/server/users.js | 7 ++ 4 files changed, 224 insertions(+), 7 deletions(-) create mode 100644 week3/prep-exercise/server/database.js diff --git a/week3/prep-exercise/README.md b/week3/prep-exercise/README.md index 4290c3ea1..6743dc8ee 100644 --- a/week3/prep-exercise/README.md +++ b/week3/prep-exercise/README.md @@ -1,12 +1,23 @@ -# Prep exercise +# Server Prep exercise week 3 -## Server +## Goals In this exercise, you will build a secure authentication and authorization system using Node.js and Express.js with four main endpoints: `register`, `login`, `getProfile`, and `logout`. The system will utilize JWT (JSON Web Tokens) for managing user sessions. Files to be modified are located in the `server` folder. -Requirements: +This will allow you to learn and practice using NodeJs and ExpressJs: + + - Securing your application with authentication and authorization principles + - Implement a standard API for users management with register, login, getProfile, and logout + - Managing user sessions using JWT (JSON Web Tokens) + +## Requirements + +You need to implement all those endpoints + +**Note:** We provide a helper to store your users so you can focus on learning the security part. + Please read more in [the next section](#database-helper) 1. Register Endpoint: @@ -16,7 +27,7 @@ Requirements: - Return a success message along with the user's ID and username upon successful registration, format: `{id: , username: }` - In case of any errors along the way, return an appropriate error message (format: `{message: }`) with a corresponding status code in the 40x range. -1. Login Endpoint: +2. Login Endpoint: - Create a `POST` endpoint `/auth/login` that allows users to log in with their registered credentials. - Verify the user's credentials by comparing the hashed password stored in memory. @@ -24,7 +35,7 @@ Requirements: - Return the JWT token to the client upon successful login, format: `{token: }` with status code 201. - In case of any errors along the way, return an appropriate error message (format: `{message: }`) with a corresponding status code in the 40x range. -1. Get Profile Endpoint: +3. Get Profile Endpoint: - Implement a `GET` endpoint `/auth/profile` that allows authenticated users to retrieve their profile information. - Extract the JWT token from the Authorization header. @@ -33,12 +44,67 @@ Requirements: - Return a message with the user's username upon successful profile retrieval. - In case of any errors along the way, return an appropriate error message (format: `{message: }`) with a status code 401 (Unauthorized). -1. Logout Endpoint: +4. Logout Endpoint: - Create a `POST` endpoint `/auth/logout` that allows users to logout and invalidate their JWT token. - No server-side token invalidation is required; the client should handle token deletion. - Return a success response with a status code indicating successful logout (e.g., 204 No Content). +## Database helper + +We understand there is a lot going on this week. To help you focus on user management, JWT and security, we decided to give you a small `database` tool. + +In [`users.js`](./users.js) you will find few lines that has been already added for you: + +```javascript +import newDatabase from './database.js' + +// Change this boolean to true if you wish to keep your +// users between restart of your application +const isPersistent = true +const database = newDatabase({isPersistent}) +``` + +### To store something + +To store something in the database you can use `database.create` + +**Important:** `database.create` will create an `id` for you + +```javascript +const theObjectIWouldLikeToStore = { + some: "object with one key" +} + +const storedObject = database.create(theObjectIWouldLikeToStore) + +console.log(storedObject) +// { +// some: "object with one key", +// id: '6a9252f7-d74a-4c6f-8076-dac277549e9b' +// } +``` + +### To get something from the database + +You can only get something by `id` using `database.getById` + +It will return the first object it finds with the passed `id` or it will return `undefined` + +```javascript +const storedObject = database.getById('6a9252f7-d74a-4c6f-8076-dac277549e9b') +// { +// some: "object with one key", +// id: '6a9252f7-d74a-4c6f-8076-dac277549e9b' +// } + +const notFoundObject = database.getById('NOT-A-VALID-ID') +// undefined +``` + + + + ## Client (optional) While you can test the endpoints of your API with Postman and/or by creating unit tests with Jest and Supertest, we have also provided a fully functional demo front-end application that demonstrates how a web token based authentication system might be used from the front-end side. The demo front-end resides in the `client` folder and is statically served by the backend. The client expects an API that meets the specification as outlined above. diff --git a/week3/prep-exercise/package.json b/week3/prep-exercise/package.json index 862e5385b..6d4a60eeb 100644 --- a/week3/prep-exercise/package.json +++ b/week3/prep-exercise/package.json @@ -6,7 +6,8 @@ "types": "module", "scripts": { "start": "nodemon ./server/app.js", - "demo": "nodemon ./server-demo/app.js" + "demo": "nodemon ./server-demo/app.js", + "db:test": "node ./server/database.js" }, "type": "module", "keywords": [], @@ -15,6 +16,7 @@ "dependencies": { "bcrypt": "^5.1.1", "express": "^4.18.2", + "lokijs": "^1.5.12", "jsonwebtoken": "^9.0.2", "uuid": "^9.0.1" }, @@ -22,3 +24,4 @@ "nodemon": "^3.1.0" } } + diff --git a/week3/prep-exercise/server/database.js b/week3/prep-exercise/server/database.js new file mode 100644 index 000000000..f7594babd --- /dev/null +++ b/week3/prep-exercise/server/database.js @@ -0,0 +1,141 @@ +/** + * Hello Student, + * + * This file is a helper file. + * Thanks to this file you do not have to care yet about how to store + * your users. + * + * HOW TO USE: Look into the README.md + * + * ## YOU DO NOT HAVE TO READ OR UNDERSTAND THIS CODE TO USE IT. + * ## Stop reading here if you just would like to use the database + * ## and focus on the register, login, getProfile, and logout :) + * ########################################################### + * ########################################################### + * ########################################################### + * ########################################################### + * ## Read After this if you intend to update this helper file. + * + * This relies on lokijs for the persistence layer and a + * simple array for the in memory one. + * + * Both makeInMemoryDb and makeNewLokiDatabase needs to return + * the same shape of object. + * Here is a typescript style signature of said object: + * + * type DbInterface = { + * create(object: T): {id: string} & T + * getById(id: string): {id: string} & T + * } + * + * lokijs: https://github.com/techfort/LokiJS + * + * You will find rudimentary tests at the end of this file, + * you can run them thanks to: + * npm run db:test + * or + * yarn db:test + */ + + +import {default as Loki} from 'lokijs' +import {v4 as uuid} from 'uuid' + +const makeInMemoryDb = () => { + const localDb = [] + + return { + create: (user) => { + const storedUser = { + ...user, + id: uuid() + } + + localDb.push(storedUser) + + return storedUser + }, + getById: (id) => { + return localDb.find(user => user.id === id) || undefined + } + } +} + +const makeNewLokiDatabase = () => { + const db = new Loki('sandbox.db'); + const users = db.addCollection('users'); + + return { + create: (user) => { + const storedUser = { + ...user, + id: uuid() + } + + users.insert(storedUser) + + return storedUser + }, + getById: (id) => { + return users.findOne({id}) || undefined + } + } +} + +/** + * Db factory + * + * @param isPersistent should it return a LokiJS based implementation or array based one? + * @returns {{ + * create: (function(*): *&{id: string}), + * getById: (function(id: string): {id: string}&*) + * }} + */ +const makeDatabase = ({isPersistent} = {isPersistent: false}) => + isPersistent ? makeNewLokiDatabase() : makeInMemoryDb() + +export default makeDatabase + +// TESTS ######################################################## + +if (process.argv[0].includes("node") && process.argv[1].includes("database.js")) { + console.log('running database tests -------------') + + // Given + const dbPersist = makeDatabase({isPersistent: true}) + const dbInMem = makeDatabase({isPersistent: false}) + + const testUser = { + name: "super", + pw: "else", + some: { + nested: "key", + and: ["an", "array"] + } + } + + // When creating user + const persistUser = dbPersist.create(testUser) + const inMemStoredUser = dbInMem.create(testUser) + + // Then + console.assert(persistUser.some.nested === testUser.some.nested, "fail to create user from persist db") + console.assert(inMemStoredUser.some.nested === testUser.some.nested, "fail to create user from in mem db") + console.assert(persistUser.id !== undefined, "persistent db returned user without id") + console.assert(inMemStoredUser.id !== undefined, "in mem user returned db without id") + + // When retrieving user + const foundPersistedUser = dbPersist.getById(persistUser.id) + const foundInMemUser = dbInMem.getById(inMemStoredUser.id) + const notFoundPersistedUser = dbPersist.getById("FAKE-ID") + const notFoundMemUser = dbPersist.getById("FAKE-ID") + + // Then + console.assert(foundPersistedUser.name === testUser.name, "retrieving user from persistent db failed") + console.assert(foundInMemUser.name === testUser.name, "retrieving user from inMem db failed") + console.assert(notFoundPersistedUser === undefined, "persistent db returned a user from an unknown ID") + console.assert(notFoundMemUser === undefined, "in mem db returned a user from an unknown ID") + + console.log('All tests performed ---------------') + console.log('No output means tests passes') +} diff --git a/week3/prep-exercise/server/users.js b/week3/prep-exercise/server/users.js index 2cbe3e2cb..fbf91e6c2 100644 --- a/week3/prep-exercise/server/users.js +++ b/week3/prep-exercise/server/users.js @@ -1,3 +1,10 @@ +import newDatabase from './database.js' + +// Change this boolean to true if you wish to keep your +// users between restart of your application +const isPersistent = false +const database = newDatabase({isPersistent}) + // Create middlewares required for routes defined in app.js // export const register = async (req, res) => {}; From ecd4cba57f1d1d5a2164dd3d92644fe6ca02ed35 Mon Sep 17 00:00:00 2001 From: Stasel <2033301+stasel@users.noreply.github.com> Date: Sun, 30 Jun 2024 14:44:24 +0200 Subject: [PATCH 29/31] Change Homework to Assignments (#638) --- {homework => assignments}/config-files/babel.config.cjs | 0 {homework => assignments}/config-files/jest.config.js | 0 week3/MAKEME.md | 3 +-- 3 files changed, 1 insertion(+), 2 deletions(-) rename {homework => assignments}/config-files/babel.config.cjs (100%) rename {homework => assignments}/config-files/jest.config.js (100%) diff --git a/homework/config-files/babel.config.cjs b/assignments/config-files/babel.config.cjs similarity index 100% rename from homework/config-files/babel.config.cjs rename to assignments/config-files/babel.config.cjs diff --git a/homework/config-files/jest.config.js b/assignments/config-files/jest.config.js similarity index 100% rename from homework/config-files/jest.config.js rename to assignments/config-files/jest.config.js diff --git a/week3/MAKEME.md b/week3/MAKEME.md index b50f7c1f4..d2f4cd7fb 100644 --- a/week3/MAKEME.md +++ b/week3/MAKEME.md @@ -37,8 +37,7 @@ Have a go by building a simple full stack chat application with an express webso We focused solely on the REST way of building an API, but there is a different way called `GraphQL`. This allows the frontend to define in their query the data that they want to get back. Very cool, but also quite complex. If you are up for a challenge, try to recreate your project using GraphQL (`express-graphql` package is probably the easiest way)! -## **SUBMIT YOUR HOMEWORK!** - +## **SUBMIT YOUR ASSIGNMENT!** After you've finished your todo list it's time to show us what you got! Upload all your files to your forked repository (same as week 1). Then make a pull request to it. If you need a refresher, take a look at the following [guide](../hand-in-assignments-guide.md) to see how it's done. From 9bc7c1bc4292c649d3361a051cfe63ce3925960f Mon Sep 17 00:00:00 2001 From: JosephineHYF <113513079+JosephineHYF@users.noreply.github.com> Date: Wed, 23 Apr 2025 14:55:17 +0200 Subject: [PATCH 30/31] Update MAKEME.md --- week1/MAKEME.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/week1/MAKEME.md b/week1/MAKEME.md index 2b5fbf315..e2bd1d275 100644 --- a/week1/MAKEME.md +++ b/week1/MAKEME.md @@ -74,7 +74,7 @@ Test out your work using Postman and make sure that any time you submit somethin If you are tired of constantly restarting your server, google the `nodemon` package to see if that will be useful for you! -## **SUBMIT YOUR HOMEWORK!** +## **Submit your assignment!** After you've finished your todo list it's time to show us what you got! Have a look at the following [guide](../hand-in-assignments-guide.md) to see how it's done. From 1bec7d5ee149c872feb31baedfa9090bd33a0eca Mon Sep 17 00:00:00 2001 From: Stasel <2033301+stasel@users.noreply.github.com> Date: Wed, 30 Apr 2025 16:41:25 +0200 Subject: [PATCH 31/31] Update MAKEME.md (#663) Update makeme for week 3 --- week3/MAKEME.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/week3/MAKEME.md b/week3/MAKEME.md index d2f4cd7fb..73afa3def 100644 --- a/week3/MAKEME.md +++ b/week3/MAKEME.md @@ -37,13 +37,3 @@ Have a go by building a simple full stack chat application with an express webso We focused solely on the REST way of building an API, but there is a different way called `GraphQL`. This allows the frontend to define in their query the data that they want to get back. Very cool, but also quite complex. If you are up for a challenge, try to recreate your project using GraphQL (`express-graphql` package is probably the easiest way)! -## **SUBMIT YOUR ASSIGNMENT!** -After you've finished your todo list it's time to show us what you got! Upload all your files to your forked repository (same as week 1). Then make a pull request to it. - -If you need a refresher, take a look at the following [guide](../hand-in-assignments-guide.md) to see how it's done. - -The assignments that needs to be submitted is the following: - -1. Project: HackYourTemperature II - -_Deadline Tuesday 23.59 CET_