From 9d636e0d61ab1da9fade81f114410e80251fbe13 Mon Sep 17 00:00:00 2001 From: Sebastian <seppizill@gmx.de> Date: Wed, 17 May 2023 10:26:22 +0200 Subject: [PATCH] Little more docs and linting for backend --- README.md | 12 + backend/.gitignore | 2 + backend/{_old-Dockerfile => Dockerfile} | 0 backend/README.md | 220 +++++++++--------- .../src/controller/cookingStepsController.ts | 4 +- .../src/controller/getRecipesForIngredient.ts | 1 - .../controller/ingredientEntriesController.ts | 3 +- .../src/controller/recipeEntriesController.ts | 1 - backend/src/index.ts | 1 + package-lock.json | 6 - 10 files changed, 130 insertions(+), 120 deletions(-) create mode 100644 README.md rename backend/{_old-Dockerfile => Dockerfile} (100%) delete mode 100644 package-lock.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..74fbfa0 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# FWE-SS-23-769544 - Advanced web technologies + +## Set up the project ("beschreibt, wie die Applikation aufzusetzen ist") +0. You will need beforehand +- Node: Webserver (https://nodejs.org/en 18.16.0) +- Postman: For API / HTTP calls & checks (https://www.postman.com/downloads/) +- Git clone this repository to your machine and move a cmd inside + - git clone https://code.fbi.h-da.de/stsezill/FWE-SS-23-769544.git + + +1. <b>Set up the backend</b>: Please follow the detailed instructions within the readme of the backend/ folder +2. <b>Set up the frontend</b>: Please follow the detailed instructions within the readme of the frontend/ folder diff --git a/backend/.gitignore b/backend/.gitignore index 11ddd8d..a554ede 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,3 +1,5 @@ node_modules +dist + # Keep environment variables out of version control .env diff --git a/backend/_old-Dockerfile b/backend/Dockerfile similarity index 100% rename from backend/_old-Dockerfile rename to backend/Dockerfile diff --git a/backend/README.md b/backend/README.md index 0fc33b2..f6aac9a 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1,6 +1,6 @@ # FWE-SS-23-769544 - BACKEND -## Set up the project (beschreibt, wie die Applikation aufzusetzen ist) +## Set up the project ("beschreibt, wie die Applikation aufzusetzen ist") 0. You will need beforehand - Node: Webserver (https://nodejs.org/en 18.16.0) - Postman: For API / HTTP calls & checks (https://www.postman.com/downloads/) @@ -8,150 +8,156 @@ - git clone https://code.fbi.h-da.de/stsezill/FWE-SS-23-769544.git -1. Run docker container with postgres -- (docker-compose down) -- (docker system prune -a --volumes) -- docker-compose up -d - -2. Move to backend/ directory -- cd backend/ - -3. Usually not in repository: Set up .env file in backend/.env with following two lines (without "- ") +1. <b>Run docker container with postgres</b> +- `(docker-compose down)` +- `(docker system prune -a --volumes)` +- `docker-compose up -d` +<br> +2. <b>Move to this backend/ directory<br>(all following commands etc. happen here in the backend module / directory)</b> +- `cd backend/` +<br> +3. <b>Usually not in repository: Set up .env file in backend/.env with following two lines (without "- ")</b> - DATABASE_URL="postgresql://sebastian:fweSS22@localhost:5432/chefkochDB?schema=public" - MY_JWT_SECRET = "MY_SECRET_SECRET" - -4. Install node modules from package.json -- npm i - -5. Create Prisma DB -- npx prisma db push --schema=src/prisma/schema.prisma - -6. Run node on localhost:3000 +<br> +4. <b>Install node modules from package.json</b> +- `npm i` +<br> +5. <b>Create Prisma DB</b> +- `npx prisma db push --schema=src/prisma/schema.prisma` +<br> +6. <b>Run node on localhost:3000</b> Note: since these are permanent containers i suggest to use multiple vs terminals -- npm run start:dev - -7. Run Prisma Studio on localhost:5555 -- npm run start:prisma - -8. Create fresh DB Schema -- npm run schema:fresh - -### Now the project is set up and you have a node backend running on localhost:3000 and a database interface on localhost:5555 to look at the data +- `npm run start:dev` +<br> +7. <b>Run Prisma Studio on localhost:5555</b> +- `npm run start:prisma` +<br> +8. <b>Create fresh DB Schema</b> +- `npm run schema:fresh` + +### Now the project is set up and you have:<br>- a node backend running on <i>localhost:3000</i><br>- a database interface on <i>localhost:5555</i> ===========================================================================<br> -## Test this project (beschreibt wie die API getestet werden kann.) -To test the application, there is a postman collection you can click through and also check out prismas localhost:5555 to view the effect on the Database live! +## Test this project ("beschreibt wie die API getestet werden kann") +To test the application, there is a postman collection you can click through. You can also check out prismas <i>localhost:5555</i> to view the effect on the Database live! 1. Open Postman +<br> 2. Click on the "Hamburger"-Icon on the very top left "File->Import..." +<br> 3. Drag & Drop the /backend/testing.postman_collection-v2.1.json into the Window - Shouldn´t be a problem, but if it does not work please also try the /backend/testing.postman_collection-v2.0.json +<br> 4. Click on left->collections and open the imported "Integration testing" - Since as a bonus I included the authorization token from the classes you first <b>must</b> run "0-Register User" and "1-Login User". If you want you can take a look at an unauthorized access with "2-Unauthorized-no-jwt-token" - +<br> 5. Now move through the folders 1-Recipe, 2-Ingredient, 3-CookingStep, 4-CustomRoutes, 5-ForeignkeyHandling from top to bottom and view the localhost:5555 for the database structure throughout the testing of hopefully all necessary cases. - -Example:<br> +<br> +Example Video ("Testingexample-Postman-Auth+Recipe.mp4"):<br> <video width="320" height="240" controls> <source src="Testingexample-Postman-Auth+Recipe.mp4" type="video/mp4"> - Your browser does not support the video tag. + Can´t load, please look at the video "Testingexample-Postman-Auth+Recipe.mp4" from the file structure </video> ===========================================================================<br> ## Usage (die Funktionalitäten beschreibt) The contents inside this backend/ folder describe a REST API written in Node.js with Typescript and Express, handeling Data in a Database described here: -<img style="float: right;" src="DB-draft.png"><br> +<img style="float: right;" src="DB-draft.png"><br><br> With this API you can view (GET), create (POST), update (PUT) and delete (DELETE) data within this database via the Prisma ORM functionality implemented within this folders code. To do that you call different routes as follows in the next paragraph more in detail. To achieve that we have following functionalities / folders, all located in /backend/src/. -Preface: The given folders and containing files each have the according functions -- entities/: Give a file structure including a node_module validator (yup) to use as schemas in the other files -- prisma/: Give interface functions to interact asynchronously with the database by selecting, updating, inserting or deleting according data -- controller/: Strongly bound to the prisma DB Interface for the regarding entity. Checks user Input and calls DB actions depending on the route it catches (get, post, put, delete) -- middleware/: just for the authentication process - injects itself between controller / routers and checks if the user - jwt token is valid for usage, hence denies or grants access -- prisma/prismaClient.ts: Instance of PrismaClient, called and used within the DBInterfaces to communicate with the DB asynchronously. +<b>Preface</b>: The given folders and containing files each have the according functions +- <b>entities/</b>: Give a file structure including a node_module validator (yup) to use as schemas in the other files +- <b>prisma/</b>: Give interface functions to interact asynchronously with the database by selecting, updating, inserting or deleting according data + - <b>prisma/prismaClient.ts</b>: Instance of PrismaClient, called and used within the DBInterfaces to communicate with the DB asynchronously. +- <b>controller/</b>: Strongly bound to the prisma DB Interface for the regarding entity. Checks user Input and calls DB actions depending on CRUD, it is called with (get, post, put, delete) +- <b>middleware/</b>: just for the authentication process - injects itself between controller / routers and checks if the user - jwt token is valid for usage, hence denies or grants access + ==========<br><br> -- DB initialization: Is done over the ./prisma/schema.prisma file with the prisma node_module -- User and Authorization: Is done over entities/UserEntity.ts, prisma/userDBInterface.ts, controller/authController.ts, middleware/auth.ts -- Recipe Selection, Creation, Updating and Deletion: Is done over entities/RecipeEntity.ts, prisma/recipeDBInterface.ts, controller/recipeEntriesController.ts -- Ingerdient Selection, Creation, Updating and Deletion: Is done over entities/IngredientEntity.ts, prisma/ingredientDBInterface.ts, controller/ingredientEntriesController.ts -- CookingSteps Selection, Creation, Updating and Deletion: Is done over entities/CookingStepEntity.ts, prisma/cookingStepDBInterface.ts, controller/cookingStepEntriesController.ts +- <b>DB initialization</b>: Is done over the ./prisma/schema.prisma file with the prisma node_module +- <b>User and Authorization</b>: Is done over entities/UserEntity.ts, prisma/userDBInterface.ts, controller/authController.ts, middleware/auth.ts +- <b>Recipe Selection, Creation, Updating and Deletion</b>: Is done over entities/RecipeEntity.ts, prisma/recipeDBInterface.ts, controller/recipeEntriesController.ts +- <b>Ingerdient Selection, Creation, Updating and Deletion</b>: Is done over entities/IngredientEntity.ts, prisma/ingredientDBInterface.ts, controller/ingredientEntriesController.ts +- <b>CookingSteps Selection, Creation, Updating and Deletion</b>: Is done over entities/CookingStepEntity.ts, prisma/cookingStepDBInterface.ts, controller/cookingStepEntriesController.ts ===========================================================================<br> ## API Routes (die Struktur der Routen auflistet) For now, the server runs on localhost:3000, and depending on the path you can manipulate the data in the database - localhost:3000/auth - - POST localhost:3000/auth/register: Register a new user with following data<br> - {"email" : "test@test.de","password":"test","fName":"Sebastian","lName":"Zill"} - - POST localhost:3000/auth/login: Login a registered user and get the jwt token to authorize access to following routes<br> - {"email": "test@test.de","password": "test"} - + - POST localhost:3000/auth/register: Register a new user with following data + `{"email" : "test@test.de","password":"test","fName":"Sebastian","lName":"Zill"}` + - POST localhost:3000/auth/login: Login a registered user and get the jwt token to authorize access to following routes + `{"email": "test@test.de","password": "test"}` +<br> - localhost:3000/recipeEntries - - POST localhost:3000/recipeEntries: Create new recipe with following input options:<br> - {"rName" : "Nudelauflauf"}<br> - {"rName" : "Nudelauflauf","rDescription" : "Nudeln im Auflauf"}<br> - {"rName" : "Nudelauflauf","rDescription" : "Nudeln im Auflauf","rImg" : "path/to/img.png"} - + - POST localhost:3000/recipeEntries: Create new recipe with following input options: + `{"rName" : "Nudelauflauf"}` + `{"rName" : "Nudelauflauf","rDescription" : "Nudeln im Auflauf"}` + `{"rName" : "Nudelauflauf","rDescription" : "Nudeln im Auflauf","rImg" : "path/to/img.png"}` + <br> - GET localhost:3000/recipeEntries: Query recipes in three modes:<br> - localhost:3000/recipeEntries: Query all recipes<br> - localhost:3000/recipeEntries/1: Query recipe with id 1<br> - localhost:3000/recipeEntries/Nudelauflauf: Query recipe with name Nudelauflauf - - - PUT localhost:3000/recipeEntries/2: Update data for recipe with id 2 with following input options:<br> - {"rName":"RecipeName","rDescription":"Pizza out of the freezer","rImg":"Pizza.png"}<br> - {"rName":"RecipeName","rDescription":"Pizza out of the freezer"}<br> - {"rName":"RecipeName","rImg":"Pizza.png"}<br> - {"rDescription":"Pizza out of the freezer","rImg":"Pizza.png"}<br> - {"rDescription":"Pizza out of the freezer"}<br> - {"rImg":"Pizza.png"} - - - DELETE localhost:3000/recipeEntries/2: Delete recipe with id 2<br> - + - localhost:3000/recipeEntries: Query all recipes<br> + - localhost:3000/recipeEntries/1: Query recipe with id 1<br> + - localhost:3000/recipeEntries/Nudelauflauf: Query recipe with name Nudelauflauf + <br> + - PUT localhost:3000/recipeEntries/2: Update data for recipe with id 2 with following input options: + `{"rName":"RecipeName","rDescription":"Pizza out of the freezer","rImg":"Pizza.png"}` + `{"rName":"RecipeName","rDescription":"Pizza out of the freezer"}` + `{"rName":"RecipeName","rImg":"Pizza.png"}` + `{"rDescription":"Pizza out of the freezer","rImg":"Pizza.png"}` + `{"rDescription":"Pizza out of the freezer"}` + `{"rImg":"Pizza.png"}` + <br> + - DELETE localhost:3000/recipeEntries/: + - localhost:3000/recipeEntries/2: Delete recipe with id 2<br> + <br> - localhost:3000/ingredientEntries - - POST localhost:3000/ingredientEntries: Create new recipe with following input options:<br> - {"iName" : "Nudeln"}<br> - + - POST localhost:3000/ingredientEntries: Create new recipe with following input options: + `{"iName" : "Nudeln"}` + <br> - GET localhost:3000/ingredientEntries: Query ingredients in three modes:<br> - localhost:3000/ingredientEntries: Query all ingredients<br> - localhost:3000/ingredientEntries/1: Query ingredient with id 1<br> - localhost:3000/ingredientEntries/Nudeln: Query ingredient with name Nudeln - - - PUT localhost:3000/ingredientEntries/2: Update data for recipe with id 2 with following input options:<br> - {"iName":"IngredientName"} - - - DELETE localhost:3000/ingredientEntries/2: Delete ingredient with id 2<br> - + - localhost:3000/ingredientEntries: Query all ingredients + - localhost:3000/ingredientEntries/1: Query ingredient with id + - localhost:3000/ingredientEntries/Nudeln: Query ingredient with name Nudeln + <br> + - PUT localhost:3000/ingredientEntries/2: Update data for recipe with id 2 with following input options: + `{"iName":"IngredientName"}` + <br> + - DELETE localhost:3000/ingredientEntries: + - localhost:3000/ingredientEntries/2: Delete ingredient with id 2<br> + <br> - localhost:3000/cookingStepEntries - - POST localhost:3000/cookingStepEntries: Create new cooking steps with following input options (iId and rId have to exist, of course!):<br> - {"description" : "Tomaten hinzugeben", "unit" : "Stück", "amount" : 2, "rId" : 1, "iId" : 2} - + - POST localhost:3000/cookingStepEntries: Create new cooking steps with following input options (iId and rId have to exist, of course!): + `{"description" : "Tomaten hinzugeben", "unit" : "Stück", "amount" : 2, "rId" : 1, "iId" : 2}` + <br> - GET localhost:3000/cookingStepEntries: Query cooking steps with following input options:<br> - null: Query all cooking steps<br> - {"rId" : 0}: Query all cooking steps for recipe with id 0<br> - {"iId" : 0}: Query all cooking steps for ingredient with id 0 - - - PUT localhost:3000/cookingStepEntries/2: Update data for cooking steps with id 2 with following input options:<br> - {"description" : "Tomaten hinzugeben", "unit" : "Stück", "amount" : 2, "rId" : 1, "iId" : 2}<br> - each field is optional! - - - DELETE localhost:3000/cookingStepEntries/2: Delete cooking steps with id 2<br> - + - `null`: Query all cooking steps + - `{"rId" : 0}`: Query all cooking steps for recipe with id 0 + - `{"iId" : 0}`: Query all cooking steps for ingredient with id 0 + <br> + - PUT localhost:3000/cookingStepEntries/2: Update data for cooking steps with id 2 with following input options (each field is optional - combinations as needed!): + `{"description" : "Tomaten hinzugeben", "unit" : "Stück", "amount" : 2, "rId" : 1, "iId" : 2}` + <br> + - DELETE localhost:3000/cookingStepEntries + - localhost:3000/cookingStepEntries/2: Delete cooking steps with id 2 + <br> - localhost:3000/recipeEntriesForIngredient/ingredient: Custom Route "Es soll eine Route geben die alle Rezepte in dem eine bestimmte Zutat existiert zurückgeben kann." - Get all recipes including their cooking steps for the ingredient "ingredient" by its name ## Archive - Long setup I did to get here -cd backend/ -npm init -y -npm i express -npm i ts-node -npm i yup -npm i typescript -D -npm i ts-node -D -npm i tsc-watch -D -npm i @types/express -D -npm i @types/node -D -npm i @types/typescript -D -npm i prisma ts-node -npm i @prisma/client -D +cd backend/<br> +npm init -y<br> +npm i express<br> +npm i ts-node<br> +npm i yup<br> +npm i typescript -D<br> +npm i ts-node -D<br> +npm i tsc-watch -D<br> +npm i @types/express -D<br> +npm i @types/node -D<br> +npm i @types/typescript -D<br> +npm i prisma ts-node<br> +npm i @prisma/client -D<br> npx tsc --init \ No newline at end of file diff --git a/backend/src/controller/cookingStepsController.ts b/backend/src/controller/cookingStepsController.ts index b2dbfbf..1f3b566 100644 --- a/backend/src/controller/cookingStepsController.ts +++ b/backend/src/controller/cookingStepsController.ts @@ -88,9 +88,7 @@ router.put("/:id", async (req, res) => { // Check if CookingStep does not exist if ((await selectCookingStepById(Number(id))) != null) { // Check if given recipe and ingredient exist - if ( - await checkIngredientAndRecipeExistence(req.body.rId, req.body.iId) - ) { + if (await checkIngredientAndRecipeExistence(req.body.rId, req.body.iId)) { // Update DB entry updateCookingStep(Number(id), req.body); res.status(202).send(req.body); diff --git a/backend/src/controller/getRecipesForIngredient.ts b/backend/src/controller/getRecipesForIngredient.ts index 157b20c..1080eb0 100644 --- a/backend/src/controller/getRecipesForIngredient.ts +++ b/backend/src/controller/getRecipesForIngredient.ts @@ -18,7 +18,6 @@ router.get("/:ingredient", async (req, res) => { var iId = null; if (myIngredient != null) iId = myIngredient.iId; - console.log(iId); if (iId != null) { // Get all recipes with this ingredient const myCookingSteps: CookingStepEntity[] = await selectAllCookingStepsForIngredient(iId); diff --git a/backend/src/controller/ingredientEntriesController.ts b/backend/src/controller/ingredientEntriesController.ts index a118a70..f1e2311 100644 --- a/backend/src/controller/ingredientEntriesController.ts +++ b/backend/src/controller/ingredientEntriesController.ts @@ -79,7 +79,6 @@ router.post("/", async (req, res) => { if (validData) { // Check if Ingredient already exists if ((await selectIngredientByName(req.body.iName)) == null) { - // Check if autoincrement id was sent wrongly // Insert into DB createIngredient(req.body); res.status(201).send(req.body); @@ -106,7 +105,7 @@ router.put("/:id", async (req, res) => { res.status(400).send({ errors: e.errors }); }); if (validData) { - // Check if ingredient does not exist + // Check if ingredient exists if ((await selectIngredientById(Number(id))) != null) { if ((await selectIngredientByName(req.body.iName)) == null) { updateIngredient(Number(id), req.body); diff --git a/backend/src/controller/recipeEntriesController.ts b/backend/src/controller/recipeEntriesController.ts index 5b86773..08318d6 100644 --- a/backend/src/controller/recipeEntriesController.ts +++ b/backend/src/controller/recipeEntriesController.ts @@ -79,7 +79,6 @@ router.post("/", async (req, res) => { if (validData) { // Check if recipe already exists if ((await selectRecipeByName(req.body.rName)) == null) { - // Check if autoincrement id was sent wrongly // Insert into DB createRecipe(req.body); res.status(201).send(req.body); diff --git a/backend/src/index.ts b/backend/src/index.ts index bd66cad..7f02bad 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -31,6 +31,7 @@ export const initializeServer = async () => { // Custom route for "Es soll eine Route geben die alle Rezepte in dem eine bestimmte Zutat existiert zurückgeben kann." app.use("/recipeEntriesForIngredient", Auth.verifyAccess, recipeControllerForIngredient); + DI.server = app.listen(PORT, () => { console.log(`Server started on port ${PORT}`); }); diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 7f55f1b..0000000 --- a/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "FWE-SS-23-769544", - "lockfileVersion": 3, - "requires": true, - "packages": {} -} -- GitLab