diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt index 22bf240..672be6e 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt @@ -98,7 +98,7 @@ data class UpdateUserRequest(val userName: String, val email: String) fun updateUser(ctx: Context, username: String, email: String) { val request = UpdateUserRequest(username, email) - return requestJson(ctx, "PUT", "/api/User/update", request) + requestJson(ctx, "PUT", "/api/User/update", request) } @Serializable diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/shopping_list.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/shopping_list.kt new file mode 100644 index 0000000..740f9fb --- /dev/null +++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/shopping_list.kt @@ -0,0 +1,20 @@ +package tech.mercantec.easyeat.helpers + +import android.content.Context +import kotlinx.serialization.Serializable + +@Serializable +data class ShoppingListItem(val id: Int, val name: String, val amount: Double?, val unit: String?, val checked: Boolean) + +fun getShoppingList(ctx: Context): Array { + return requestJson>(ctx, "GET", "/api/ShoppingList/get", null) +} + +@Serializable +data class AddShoppingItemRequest(val name: String, val amount: Double?, val unit: String?, val checked: Boolean) + +fun addShoppingItem(ctx: Context, name: String, amount: Double?, unit: String?) { + val request = AddShoppingItemRequest(name, amount, unit, false) + + requestJson(ctx, "POST", "/api/ShoppingList/add", request) +} diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/profile/EditProfileActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/profile/EditProfileActivity.kt index acf8a4c..ff78d51 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/profile/EditProfileActivity.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/profile/EditProfileActivity.kt @@ -1,6 +1,7 @@ package tech.mercantec.easyeat.ui.profile import android.app.ProgressDialog +import android.content.Intent import android.os.Bundle import android.widget.Button import android.widget.EditText @@ -43,6 +44,11 @@ class EditProfileActivity : AppCompatActivity() { } } + val intent = Intent() + intent.putExtra("username", username) + intent.putExtra("email", email) + setResult(RESULT_OK, intent) + finish() } } diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/profile/ProfileFragment.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/profile/ProfileFragment.kt index 6f735f5..7c8e27c 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/profile/ProfileFragment.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/profile/ProfileFragment.kt @@ -1,11 +1,15 @@ package tech.mercantec.easyeat.ui.profile +import android.app.Activity import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.view.children import androidx.fragment.app.Fragment import tech.mercantec.easyeat.ui.auth.WelcomeActivity import tech.mercantec.easyeat.databinding.FragmentProfileBinding @@ -16,8 +20,25 @@ import tech.mercantec.easyeat.helpers.logout import kotlin.concurrent.thread class ProfileFragment : Fragment() { + private lateinit var launcher: ActivityResultLauncher + private lateinit var binding: FragmentProfileBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + if (it.data == null || it.resultCode != Activity.RESULT_OK) return@registerForActivityResult + + binding.username.text = it.data!!.getStringExtra("username") + binding.email.text = it.data!!.getStringExtra("email") + } + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - val binding = FragmentProfileBinding.inflate(inflater, container, false) + binding = FragmentProfileBinding.inflate(inflater, container, false) + + binding.layout.children.forEach { it.visibility = View.GONE } + binding.loading.visibility = View.VISIBLE thread { val userInfo: UserInfoResponse @@ -29,6 +50,11 @@ class ProfileFragment : Fragment() { } return@thread + } finally { + activity?.runOnUiThread { + binding.layout.children.forEach { it.visibility = View.VISIBLE } + binding.loading.visibility = View.GONE + } } activity?.runOnUiThread { @@ -37,9 +63,10 @@ class ProfileFragment : Fragment() { binding.editProfile.setOnClickListener { val intent = Intent(activity, EditProfileActivity::class.java) - intent.putExtra("username", userInfo.userName) - intent.putExtra("email", userInfo.email) - startActivity(intent) + intent.putExtra("username", binding.username.text) + intent.putExtra("email", binding.email.text) + + launcher.launch(intent) } } } diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt index fef40ae..878cab7 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt @@ -1,15 +1,21 @@ package tech.mercantec.easyeat.ui.shopping_list import android.app.AlertDialog +import android.app.Dialog import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter +import android.widget.EditText import android.widget.Spinner +import android.widget.Toast import androidx.fragment.app.Fragment import tech.mercantec.easyeat.R import tech.mercantec.easyeat.databinding.FragmentShoppingListBinding +import tech.mercantec.easyeat.helpers.ApiRequestException +import tech.mercantec.easyeat.helpers.addShoppingItem +import kotlin.concurrent.thread class ShoppingListFragment : Fragment() { @@ -32,12 +38,33 @@ class ShoppingListFragment : Fragment() { val dialog = AlertDialog.Builder(activity) .setView(view) - .setPositiveButton(R.string.add_label, { dialog, id -> + .setPositiveButton(R.string.add_label) { dialog, id -> + val dialog = dialog as AlertDialog - }) - .setNegativeButton(R.string.cancel_label, { dialog, id -> + val amount = view.findViewById(R.id.amount).text.toString().toDouble() + val unit = view.findViewById(R.id.unit_selector).selectedItem.toString().ifEmpty { null } + val name = view.findViewById(R.id.name).text.toString() + + dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false + dialog.getButton(AlertDialog.BUTTON_NEGATIVE).isEnabled = false + + thread { + try { + addShoppingItem(requireContext(), name, amount, unit) + } catch (e: ApiRequestException) { + activity?.runOnUiThread { + Toast.makeText(context, e.message, Toast.LENGTH_LONG).show() + } + } finally { + activity?.runOnUiThread { + dialog.dismiss() + } + } + } + } + .setNegativeButton(R.string.cancel_label) { dialog, _ -> dialog.cancel() - }) + } .create() dialog.show() diff --git a/app/app/src/main/res/layout/dialog_add_to_shopping_list.xml b/app/app/src/main/res/layout/dialog_add_to_shopping_list.xml index 3d9ae2d..db8d297 100644 --- a/app/app/src/main/res/layout/dialog_add_to_shopping_list.xml +++ b/app/app/src/main/res/layout/dialog_add_to_shopping_list.xml @@ -30,12 +30,14 @@ android:layout_height="wrap_content" android:layout_below="@id/amount_label" android:layout_alignParentStart="true" + android:inputType="numberDecimal" + android:importantForAutofill="no" android:hint="@string/ingredient_amount_hint" /> diff --git a/app/app/src/main/res/layout/fragment_profile.xml b/app/app/src/main/res/layout/fragment_profile.xml index f2708c8..be15d10 100644 --- a/app/app/src/main/res/layout/fragment_profile.xml +++ b/app/app/src/main/res/layout/fragment_profile.xml @@ -1,6 +1,7 @@ Change password Log out + g kg ml diff --git a/backend/API/BusinessLogic/RecipeLogic.cs b/backend/API/BusinessLogic/RecipeLogic.cs index 8d02900..48aaa3f 100644 --- a/backend/API/BusinessLogic/RecipeLogic.cs +++ b/backend/API/BusinessLogic/RecipeLogic.cs @@ -16,8 +16,8 @@ namespace API.BusinessLogic /// /// Gets all the recipes from _dbaccess and checks if there are any /// - /// - /// + /// the usér connected to the recipes + /// a list of recipes in a ok objectresult public async Task GetRecipes(int userId) { var recipes = await _dbAccess.ReadRecipes(userId); @@ -26,7 +26,7 @@ namespace API.BusinessLogic return new OkObjectResult(recipes); } - // + // Gets a specifik with recipe with the ingredient and directions public async Task GetRecipe(int recipeId) { var recipe = await _dbAccess.ReadRecipe(recipeId); @@ -36,6 +36,13 @@ namespace API.BusinessLogic return new OkObjectResult(recipe); } + /// + /// Creates a recipe and checks if the recipe name is in use + /// Converts the recipeDTO to a normal recipe + /// + /// The recipeDTO that does not include all the id tags + /// The user that is going to get that recipe + /// returns what recipedbaccess gives back public async Task CreateRecipe(RecipeDTO recipe, int userId) { var recipes = await _dbAccess.ReadRecipes(userId); @@ -71,6 +78,13 @@ namespace API.BusinessLogic return await _dbAccess.CreateRecipe(dish, userId); } + /// + /// Updates the recipe that is saved in the db + /// + /// The updated recipe + /// The recipeId on the recipe to be updated + /// The userÍd of the owner of the to be updated recipe + /// returns what recipedbaccess gives back public async Task EditRecipe(RecipeDTO recipe, int recipeId, int userId) { var recipes = await _dbAccess.ReadRecipes(userId); @@ -105,13 +119,14 @@ namespace API.BusinessLogic return await _dbAccess.UpdateRecipe(dish); } + // Deletes the recipe public async Task DeleteRecipe(int recipeId) { var recipe = await _dbAccess.ReadRecipe(recipeId); - if (recipe != null) { return await _dbAccess.DeleteUser(recipe); } + if (recipe != null) { return await _dbAccess.DeleteRecipe(recipe); } - return new ConflictObjectResult(new { message = "Invalid user" }); + return new ConflictObjectResult(new { message = "Invalid recipe" }); } } } diff --git a/backend/API/BusinessLogic/ShoppingListLogic.cs b/backend/API/BusinessLogic/ShoppingListLogic.cs index 53f2da9..1651896 100644 --- a/backend/API/BusinessLogic/ShoppingListLogic.cs +++ b/backend/API/BusinessLogic/ShoppingListLogic.cs @@ -15,6 +15,7 @@ namespace API.BusinessLogic _recipeDBAccess = recipeDBAccess; } + // Reads the current shooping list of the user public async Task ReadShoppingList(int userId) { var user = await _dbAccess.ReadShoppingList(userId); @@ -22,6 +23,7 @@ namespace API.BusinessLogic return new OkObjectResult(user.ShoppingList); } + // Adds an item to the shoppinglist and checks if the unit should be changed and if the name is the same as an item already on the shoppinglist public async Task AddItemToShoppingList(ShoppingListItemDTO listItemDTO, int userId) { var user = await _dbAccess.ReadShoppingList(userId); @@ -42,12 +44,12 @@ namespace API.BusinessLogic item.Amount = (item.Amount / 1000) + listItemDTO.Amount; item.Unit = "kg"; } - else if (item.Unit == "ml" && item.Unit == "l") + else if (item.Unit == "ml" && listItemDTO.Unit == "l") { item.Amount = (item.Amount / 1000) + listItemDTO.Amount; item.Unit = "l"; } - else if (item.Unit == "dl" && item.Unit == "l") + else if (item.Unit == "dl" && listItemDTO.Unit == "l") { item.Amount = (item.Amount / 10) + listItemDTO.Amount; item.Unit = "l"; @@ -85,6 +87,7 @@ namespace API.BusinessLogic return await _dbAccess.UpdateShoppingList(user); } + // Gets the shoppinglist and tries to find the item and when it does it checks/unchecks that item public async Task CheckItemInShoppingList(int userId, int itemId) { var user = await _dbAccess.ReadShoppingList(userId); @@ -96,6 +99,7 @@ namespace API.BusinessLogic return await _dbAccess.UpdateShoppingList(user); } + // Updates an item on the shopping list to what the user specified public async Task UpdateItemInShoppingList(int userId, int itemId, ShoppingListItemDTO listItemDTO) { var user = await _dbAccess.ReadShoppingList(userId); @@ -125,6 +129,7 @@ namespace API.BusinessLogic return await _dbAccess.UpdateShoppingList(user); } + // Deletes an item from the shopping list if it is on the users shoppinglist public async Task DeleteItemInShoppingList(int userId, int itemId) { var user = await _dbAccess.ReadShoppingList(userId); @@ -136,6 +141,7 @@ namespace API.BusinessLogic return await _dbAccess.UpdateShoppingList(user); } + // Adds an entire recipes ingredients to the shoppinglist public async Task AddRecipeToShoppingList(int userId, int recipeId) { var user = await _dbAccess.ReadShoppingList(userId); diff --git a/backend/API/BusinessLogic/UserLogic.cs b/backend/API/BusinessLogic/UserLogic.cs index 2a93d55..f32bd2e 100644 --- a/backend/API/BusinessLogic/UserLogic.cs +++ b/backend/API/BusinessLogic/UserLogic.cs @@ -23,6 +23,7 @@ namespace API.BusinessLogic _configuration = configuration; } + // Gets an user from their id public async Task GetUser(int userId) { User user = await _dbAccess.ReadUser(userId); @@ -31,6 +32,7 @@ namespace API.BusinessLogic return new OkObjectResult(new { user.Id, user.UserName, user.Email }); } + // Checks if the userdata is ok before the user is created and creats the othere list's that the user have public async Task RegisterUser(CreateUserDTO userDTO) { if (!EmailCheck(userDTO.Email)) @@ -71,6 +73,7 @@ namespace API.BusinessLogic return await _dbAccess.CreateUser(user); } + // Checks if the username/email matches the password and generates a jwttoken if it is correct public async Task Login(LoginDTO loginDTO) { var user = await _dbAccess.ReadUserForLogin(loginDTO.EmailUsr); @@ -91,6 +94,7 @@ namespace API.BusinessLogic return new ConflictObjectResult(new { message = "Invalid password" }); } + // Checks if the username or email is already in use and changes them if they are diffrent from before public async Task EditProfile(UpdateUserDTO userDTO, int userId) { var profile = await _dbAccess.ReadUser(userId); @@ -142,6 +146,7 @@ namespace API.BusinessLogic return await _dbAccess.UpdateUser(profile); } + // Checks if the old password is correct and then it checks if the password is secure enough public async Task ChangePassword(ChangePasswordDTO passwordDTO, int userId) { var user = await _dbAccess.ReadUser(userId); @@ -167,6 +172,7 @@ namespace API.BusinessLogic return await _dbAccess.UpdatePassword(user); } + // Checks if the user exist and it deletes that user public async Task DeleteUser(int userId) { var user = await _dbAccess.ReadUserForDelete(userId); @@ -176,6 +182,7 @@ namespace API.BusinessLogic return new ConflictObjectResult(new { message = "Invalid user" }); } + // Checks if the refreshtoken is correct and if it is it generates a new jwttoken and refreshtoken public async Task RefreshToken(string refreshToken) { User user = await _dbAccess.ReadUserByRefreshToken(refreshToken); @@ -185,6 +192,7 @@ namespace API.BusinessLogic return new OkObjectResult(new { token = jwtToken, refreshToken = user.RefreshToken }); } + // Checks if the password is up to our security standard private bool PasswordSecurity(string password) { var hasMinimum8Chars = new Regex(@".{8,}"); @@ -192,11 +200,19 @@ namespace API.BusinessLogic return hasMinimum8Chars.IsMatch(password); } + // Checks if the email has all the things an email should have private bool EmailCheck(string email) { return new Regex(@".+@.+\..+").IsMatch(email); } + /// + /// Generates a hash from a salt and input using the algorithm that is provided + /// + /// This is the input that is supposed to be hashed + /// This is the alogorithm that is used to encrypt the input + /// This is something extra added to make the hashed input more unpredictable + /// The hashed input private static string ComputeHash(string input, HashAlgorithm algorithm, string salt) { Byte[] inputBytes = Encoding.UTF8.GetBytes(input); @@ -212,6 +228,7 @@ namespace API.BusinessLogic return BitConverter.ToString(hashedBytes); } + // Generates a jwttoken that contains the users id and username and a unique identifier that is valid for 1 hour private string GenerateJwtToken(User user) { var claims = new[] @@ -235,6 +252,7 @@ namespace API.BusinessLogic return new JwtSecurityTokenHandler().WriteToken(token); } + // Generate a new refreshtoken that expire after 30 days private async Task UpdateRefreshToken(User user) { user.RefreshToken = Guid.NewGuid().ToString(); diff --git a/backend/API/Controllers/RecipeController.cs b/backend/API/Controllers/RecipeController.cs index 7d4fe6a..76a49d5 100644 --- a/backend/API/Controllers/RecipeController.cs +++ b/backend/API/Controllers/RecipeController.cs @@ -20,6 +20,10 @@ namespace API.Controllers _openAiRecipes = openAiRecipes; } + /// + /// Gets the userId from the jwt token amd returns a list of recipes without their ingredients and directions + /// + /// returns a okobjectresult with a list of recipes if it fails it returns a confliftobjectresult with a message of why it failed [Authorize] [HttpGet("getall")] public async Task ReadRecipes() @@ -30,6 +34,11 @@ namespace API.Controllers return await _recipeLogic.GetRecipes(userId); } + /// + /// Gets a specifik recipe including the ingredients and directions + /// + /// The recipe that is you want + /// returns a okobjectresult with a recipe if it fails it returns a confliftobjectresult with a message of why it failed [Authorize] [HttpGet("get/{recipeId}")] public async Task ReadRecipe(int recipeId) @@ -37,6 +46,11 @@ namespace API.Controllers return await _recipeLogic.GetRecipe(recipeId); } + /// + /// Creates a recipe and adds it to the users recipes + /// + /// The recipe to be added + /// returns a okobjectresult with a boolean that is true if it fails it returns a confliftobjectresult with a message of why it failed [Authorize] [HttpPost("create")] public async Task CreateRecipe([FromBody] RecipeDTO recipe) @@ -47,6 +61,12 @@ namespace API.Controllers return await _recipeLogic.CreateRecipe(recipe, userId); } + /// + /// Edits a recipe + /// + /// the edited recipe + /// the recipe to be edited + /// returns a okobjectresult with a boolean that is true if it fails it returns a confliftobjectresult with a message of why it failed [Authorize] [HttpPut("edit/{recipeId}")] public async Task EditRecipe([FromBody] RecipeDTO recipe, int recipeId) @@ -57,6 +77,11 @@ namespace API.Controllers return await _recipeLogic.EditRecipe(recipe, recipeId, userId); } + /// + /// Deletess a recipe + /// + /// the id of the recipe to be deleted + /// returns a okobjectresult with a boolean that is true if it fails it returns a confliftobjectresult with a message of why it failed [Authorize] [HttpDelete("delete/{recipeId}")] public async Task DeleteRecipe(int recipeId) @@ -64,6 +89,11 @@ namespace API.Controllers return await _recipeLogic.DeleteRecipe(recipeId); } + /// + /// Generates a recipe using chatgpt + /// + /// Contains all the infomation that is needed to generate a recipe + /// returns a list of generated recipes [Authorize] [HttpPost("chatbot")] public async Task GenerateRecipe([FromBody] GenerateRecipeDTO recipeDTO) diff --git a/backend/API/Controllers/ShoppingListController.cs b/backend/API/Controllers/ShoppingListController.cs index 7db1bd4..ddeb6c1 100644 --- a/backend/API/Controllers/ShoppingListController.cs +++ b/backend/API/Controllers/ShoppingListController.cs @@ -1,6 +1,7 @@  using API.BusinessLogic; using API.Models.ShoppingListModels; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Security.Claims; @@ -17,6 +18,11 @@ namespace API.Controllers _shoppingListLogic = shoppingListLogic; } + /// + /// Gets the entire shoppinglist connected to the user + /// + /// returns a list of shoppinglist items if it fails it returns a confliftobjectresult with a message of why it failed + [Authorize] [HttpGet("get")] public async Task ReadShoppingList() { @@ -26,6 +32,12 @@ namespace API.Controllers return await _shoppingListLogic.ReadShoppingList(userId); } + /// + /// Adds an item to the shopping list + /// + /// + /// + [Authorize] [HttpPost("add")] public async Task AddItem([FromBody] ShoppingListItemDTO listItemDTO) { @@ -35,6 +47,12 @@ namespace API.Controllers return await _shoppingListLogic.AddItemToShoppingList(listItemDTO, userId); } + /// + /// Checks/Unchecks an item on the shoppinglist + /// + /// The item to be checked/unchecked + /// returns a okobjectresult with a boolean that is true if it fails it returns a confliftobjectresult with a message of why it failed + [Authorize] [HttpPut("check")] public async Task CheckItem(int itemId) { @@ -44,6 +62,13 @@ namespace API.Controllers return await _shoppingListLogic.CheckItemInShoppingList(userId, itemId); } + /// + /// Edits an item on the shoppinglist + /// + /// The edited item + /// The item to be edited + /// returns a okobjectresult with a boolean that is true if it fails it returns a confliftobjectresult with a message of why it failed + [Authorize] [HttpPut("update")] public async Task UpdateItem([FromBody] ShoppingListItemDTO listItemDTO, int itemId) { @@ -53,6 +78,12 @@ namespace API.Controllers return await _shoppingListLogic.UpdateItemInShoppingList(userId, itemId, listItemDTO); } + /// + /// Deletes an item on the shoppinglist + /// + /// The item to be deleted + /// returns a okobjectresult with a boolean that is true if it fails it returns a confliftobjectresult with a message of why it failed + [Authorize] [HttpDelete("delete")] public async Task DeleteItem(int itemId) { @@ -62,6 +93,12 @@ namespace API.Controllers return await _shoppingListLogic.DeleteItemInShoppingList(userId, itemId); } + /// + /// Add an entire recipes ingredients to the shoppinglist + /// + /// The recipes ingredients to be added + /// returns a okobjectresult with a boolean that is true if it fails it returns a confliftobjectresult with a message of why it failed + [Authorize] [HttpPost("recipeadd")] public async Task AddARecipesItems(int recipeId) { diff --git a/backend/API/Controllers/UserController.cs b/backend/API/Controllers/UserController.cs index 8bb69eb..28e7ebe 100644 --- a/backend/API/Controllers/UserController.cs +++ b/backend/API/Controllers/UserController.cs @@ -17,6 +17,10 @@ namespace API.Controllers _userLogic = userLogic; } + /// + /// Gets the users email and username + /// + /// returns the users email, username and Id [Authorize] [HttpGet("get")] public async Task ReadUser() @@ -27,18 +31,33 @@ namespace API.Controllers return await _userLogic.GetUser(userId); } + /// + /// Logins a user + /// + /// The users login credentials + /// Returns a jwttoken their username, id and a refreshtoken [HttpPost("login")] public async Task Login([FromBody] LoginDTO loginDTO) { return await _userLogic.Login(loginDTO); } + /// + /// Create a new user + /// + /// contains the username email and password + /// returns a okobjectresult with a boolean that is true if it fails it returns a confliftobjectresult with a message of why it failed [HttpPost("create")] public async Task CreateUser([FromBody] CreateUserDTO userDTO) { return await _userLogic.RegisterUser(userDTO); } + /// + /// Changes the password of the user + /// + /// Contains the old password and the new one + /// returns a okobjectresult with a boolean that is true if it fails it returns a confliftobjectresult with a message of why it failed [Authorize] [HttpPut("change-password")] public async Task ChangePassword([FromBody] ChangePasswordDTO passwordDTO) @@ -49,6 +68,11 @@ namespace API.Controllers return await _userLogic.ChangePassword(passwordDTO, userId); } + /// + /// Edits the email and username of the user + /// + /// The updated username and email + /// returns a okobjectresult with a boolean that is true if it fails it returns a confliftobjectresult with a message of why it failed [Authorize] [HttpPut("update")] public async Task UpdateUser([FromBody] UpdateUserDTO userDTO) @@ -59,6 +83,10 @@ namespace API.Controllers return await _userLogic.EditProfile(userDTO, userId); } + /// + /// Deletes the user + /// + /// returns a okobjectresult with a boolean that is true if it fails it returns a confliftobjectresult with a message of why it failed [Authorize] [HttpDelete("delete")] public async Task DeleteUser() @@ -69,6 +97,11 @@ namespace API.Controllers return await _userLogic.DeleteUser(userId); } + /// + /// For when the jwt token is outdated + /// + /// contains a string with the refreshtoken + /// returns a new refreshtoken and new jwt token [HttpPost("refreshtoken")] public async Task RefreashToken([FromBody] RefreshTokenDTO refreshToken) { diff --git a/backend/API/DBAccess/RecipeDBaccess.cs b/backend/API/DBAccess/RecipeDBaccess.cs index ef1ee81..403a878 100644 --- a/backend/API/DBAccess/RecipeDBaccess.cs +++ b/backend/API/DBAccess/RecipeDBaccess.cs @@ -14,6 +14,7 @@ namespace API.DBAccess _context = context; } + // Reads the users recipes public async Task> ReadRecipes(int userId) { var recipes = await _context.Users.Include(p => p.Recipes).FirstOrDefaultAsync(u => u.Id == userId); @@ -21,11 +22,13 @@ namespace API.DBAccess return recipes.Recipes; } + // Returns a specifik recipe public async Task ReadRecipe(int recipeId) { - return await _context.Recipes.Include(r => r.Ingredients).FirstOrDefaultAsync(r => r.Id == recipeId); + return await _context.Recipes.Include(r => r.Ingredients).Include(r => r.Directions).FirstOrDefaultAsync(r => r.Id == recipeId); } + // Adds a new recipe to the database public async Task CreateRecipe(Recipe recipe, int userId) { var recipes = await _context.Users.Include(p => p.Recipes).FirstOrDefaultAsync(u => u.Id == userId); @@ -39,6 +42,7 @@ namespace API.DBAccess return new ConflictObjectResult(new { message = "Could not save to database" }); } + // Updates the recipe in the database public async Task UpdateRecipe(Recipe recipe) { _context.Entry(recipe).State = EntityState.Modified; @@ -50,7 +54,8 @@ namespace API.DBAccess return new ConflictObjectResult(new { message = "Could not save to database" }); } - public async Task DeleteUser(Recipe recipe) + // Deletes the recipe from the database + public async Task DeleteRecipe(Recipe recipe) { _context.Recipes.Remove(recipe); bool saved = await _context.SaveChangesAsync() >= 0; diff --git a/backend/API/DBAccess/ShoppingListDBAccess.cs b/backend/API/DBAccess/ShoppingListDBAccess.cs index f77069d..a9e7c68 100644 --- a/backend/API/DBAccess/ShoppingListDBAccess.cs +++ b/backend/API/DBAccess/ShoppingListDBAccess.cs @@ -14,6 +14,8 @@ namespace API.DBAccess { _context = context; } + + // Read the shoppinglist connected to the user public async Task ReadShoppingList(int userId) { var user = await _context.Users.Include(u => u.ShoppingList).FirstOrDefaultAsync(u => u.Id == userId); @@ -21,13 +23,14 @@ namespace API.DBAccess return user; } + // Updates the shoppinglist public async Task UpdateShoppingList(User user) { _context.Entry(user).State = EntityState.Modified; bool saved = await _context.SaveChangesAsync() >= 1; - if (saved) { return new OkObjectResult(user); } + if (saved) { return new OkObjectResult(true); } return new ConflictObjectResult(new { message = "Could not save to database" }); } diff --git a/backend/API/DBAccess/UserDBAccess.cs b/backend/API/DBAccess/UserDBAccess.cs index 9118433..833db4e 100644 --- a/backend/API/DBAccess/UserDBAccess.cs +++ b/backend/API/DBAccess/UserDBAccess.cs @@ -13,16 +13,19 @@ namespace API.DBAccess _context = context; } + // Reads the user from the database public async Task ReadUser(int userId) { return await _context.Users.FirstOrDefaultAsync(u => u.Id == userId); } + // Reads all the users from the database public async Task> ReadAllUsers() { return await _context.Users.ToListAsync(); } + // Searches for a user with the username public async Task UserNameInUse(string username) { var user = await _context.Users.FirstOrDefaultAsync(u => u.UserName == username); @@ -30,6 +33,7 @@ namespace API.DBAccess return true; } + // Searches for a user with the email public async Task EmailInUse(string email) { var user = await _context.Users.FirstOrDefaultAsync(u => u.UserName == email); @@ -37,16 +41,19 @@ namespace API.DBAccess return true; } + // Searches for a user with refreshtoken and returns that user public async Task ReadUserByRefreshToken(string refreshToken) { return await _context.Users.FirstOrDefaultAsync(u => u.RefreshToken == refreshToken); } + // Gets all the data for a user public async Task ReadUserForDelete(int userId) { - return await _context.Users.FirstOrDefaultAsync(u => u.Id == userId); + return await _context.Users.Include(u => u.Recipes).ThenInclude(r => r.Ingredients).Include(u => u.Recipes).ThenInclude(r => r.Directions).Include(u => u.ShoppingList).FirstOrDefaultAsync(u => u.Id == userId); } + // Gets a user according to either the email or username public async Task ReadUserForLogin(string emailOrUsername) { if (emailOrUsername.Contains("@")) @@ -59,6 +66,7 @@ namespace API.DBAccess } } + // Adds a new user to the database public async Task CreateUser(User user) { _context.Users.Add(user); @@ -71,17 +79,19 @@ namespace API.DBAccess } + // Updates the user in the database public async Task UpdateUser(User user) { _context.Entry(user).State = EntityState.Modified; bool saved = await _context.SaveChangesAsync() == 1; - if (saved) { return new OkObjectResult(user); } + if (saved) { return new OkObjectResult(true); } return new ConflictObjectResult(new { message = "Could not save to database" }); } + // Updates the password in the database public async Task UpdatePassword(User user) { _context.Entry(user).State = EntityState.Modified; @@ -93,6 +103,7 @@ namespace API.DBAccess return new ConflictObjectResult(new { message = "Could not save to database" }); } + // Deletes the user from the database public async Task DeleteUser(User user) { _context.Users.Remove(user);