From c02c47efcb00782feb1461534923023a711d4f15 Mon Sep 17 00:00:00 2001 From: sowgro Date: Sun, 2 Mar 2025 11:22:48 -0500 Subject: First attempt at an authentication system. --- ufund-api/data/cupboard.json | 9 ++- ufund-api/data/userAuths.json | 7 +++ ufund-api/data/users.json | 13 ++++- .../api/ufundapi/controller/AuthController.java | 54 ++++++++++++++++++ .../api/ufundapi/controller/UserController.java | 64 ++++++++++++++-------- .../java/com/ufund/api/ufundapi/model/User.java | 39 +++++++------ .../com/ufund/api/ufundapi/model/UserAuth.java | 43 +++++++++++++++ .../api/ufundapi/persistence/UserAuthDAO.java | 23 ++++++++ .../api/ufundapi/persistence/UserAuthFIleDAO.java | 62 +++++++++++++++++++++ .../ufund/api/ufundapi/persistence/UserDAO.java | 16 +++--- .../api/ufundapi/persistence/UserFileDAO.java | 29 ++++------ .../ufundapi/controller/UserControllerTest.java | 6 +- .../com/ufund/api/ufundapi/model/UserTest.java | 2 +- .../api/ufundapi/persistence/UserFileDAOTest.java | 8 +-- ufund-ui/src/app/app.component.html | 4 +- ufund-ui/src/app/app.component.ts | 21 ++++++- .../src/app/components/login/login.component.html | 8 +-- .../src/app/components/login/login.component.ts | 14 ++++- ufund-ui/src/app/models/Need.ts | 2 +- ufund-ui/src/app/models/User.ts | 2 +- ufund-ui/src/app/services/cupboard.service.ts | 3 +- ufund-ui/src/app/services/users.service.ts | 40 ++++++++++++-- 22 files changed, 373 insertions(+), 96 deletions(-) create mode 100644 ufund-api/data/userAuths.json create mode 100644 ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java create mode 100644 ufund-api/src/main/java/com/ufund/api/ufundapi/model/UserAuth.java create mode 100644 ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthDAO.java create mode 100644 ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java diff --git a/ufund-api/data/cupboard.json b/ufund-api/data/cupboard.json index bb7ec03..abba017 100644 --- a/ufund-api/data/cupboard.json +++ b/ufund-api/data/cupboard.json @@ -1,3 +1,10 @@ [ - {"name":"Money for coral","id":1,"maxGoal":100.0,"type":"MONETARY","filterAttributes":null,"Current":0.0} + { + "name": "Money for coral", + "id": 1, + "maxGoal": 100.0, + "type": "MONETARY", + "filterAttributes": null, + "Current": 0.0 + } ] \ No newline at end of file diff --git a/ufund-api/data/userAuths.json b/ufund-api/data/userAuths.json new file mode 100644 index 0000000..e451862 --- /dev/null +++ b/ufund-api/data/userAuths.json @@ -0,0 +1,7 @@ +[ + { + "key": "eeea7b02-7265-4a26-96de-a8ad1860c533", + "username": "phil", + "expiration": "2025-03-31T23:04:47.455490668" + } +] \ No newline at end of file diff --git a/ufund-api/data/users.json b/ufund-api/data/users.json index 4e98a14..ae575b1 100644 --- a/ufund-api/data/users.json +++ b/ufund-api/data/users.json @@ -1 +1,12 @@ -[{"name":"steve","password":null}] \ No newline at end of file +[ + { + "username": "phil", + "passwordHash": -1054080181, + "basket": [] + }, + { + "username": "tbone", + "passwordHash": 97526364, + "basket": [] + } +] \ No newline at end of file diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java new file mode 100644 index 0000000..aa27e3f --- /dev/null +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java @@ -0,0 +1,54 @@ +package com.ufund.api.ufundapi.controller; + +import com.ufund.api.ufundapi.model.UserAuth; +import com.ufund.api.ufundapi.persistence.UserAuthDAO; +import com.ufund.api.ufundapi.persistence.UserDAO; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Map; + +@RestController +@RequestMapping("auth") +public class AuthController { + private final UserDAO userDAO; + private final UserAuthDAO userAuthDAO; + + public AuthController(UserDAO userDAO, UserAuthDAO userAuthDAO) { + this.userDAO = userDAO; + this.userAuthDAO = userAuthDAO; + } + + /** + * Attempts to log in as a user + * @param params A map/json object in the format {username: string, password: string} + * @return An api key if the auth was successful, null otherwise + */ + @PostMapping("") + public ResponseEntity login(@RequestBody Map params) { + String username = params.get("username"); + String password = params.get("password"); + try { + var usr = userDAO.getUser(username); + if (usr == null || !usr.verifyPassword(password)) { + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); + } + var userAuth = UserAuth.generate(username); + userAuthDAO.addUserAuth(userAuth); + return new ResponseEntity<>(userAuth.getKey(), HttpStatus.OK); + } catch (IOException ex) { + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + /** + * TODO + * @return + */ + @DeleteMapping("") + public ResponseEntity logout() { + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } +} diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java index 4e5f156..aa9598d 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java @@ -1,19 +1,14 @@ package com.ufund.api.ufundapi.controller; import java.io.IOException; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import com.ufund.api.ufundapi.persistence.UserAuthDAO; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import com.ufund.api.ufundapi.model.User; import com.ufund.api.ufundapi.persistence.UserDAO; @@ -21,30 +16,34 @@ import com.ufund.api.ufundapi.persistence.UserDAO; @RestController @RequestMapping("users") public class UserController { - private static final Logger LOG = Logger.getLogger(CupboardController.class.getName()); + private static final Logger LOG = Logger.getLogger(UserController.class.getName()); private final UserDAO UserDAO; + private final UserAuthDAO userAuthDAO; /** * Create a user controller to receive REST signals * * @param userDAO The Data Access Object */ - public UserController(UserDAO userDAO) { + public UserController(UserDAO userDAO, UserAuthDAO userAuthDAO) { this.UserDAO = userDAO; + this.userAuthDAO = userAuthDAO; } /** * Creates a User with the provided object * - * @param user The user to create * @return OK response and the user if it was successful, INTERNAL_SERVER_ERROR * otherwise */ @PostMapping("") - public ResponseEntity createUser(@RequestBody User user) { + public ResponseEntity createUser(@RequestBody Map params) { + String username = params.get("username"); + String password = params.get("password"); + try { - if (UserDAO.createUser(user) != null) { - return new ResponseEntity<>(user, HttpStatus.CREATED); + if (UserDAO.addUser(User.create(username, password)) != null) { + return new ResponseEntity<>(true, HttpStatus.CREATED); } else { return new ResponseEntity<>(HttpStatus.CONFLICT); } @@ -62,14 +61,19 @@ public class UserController { * ResponseEntity with HTTP status of NOT_FOUND if not found
* ResponseEntity with HTTP status of INTERNAL_SERVER_ERROR otherwise */ - @GetMapping("/{name}") - public ResponseEntity getUser(@PathVariable String name) { - LOG.log(Level.INFO, "GET /user/{0}", name); + @GetMapping("/{username}") + public ResponseEntity getUser(@PathVariable String username, @RequestHeader("jelly-api-key") String key) { + LOG.log(Level.INFO, "GET /user/{0}", username); + + var userAuth = userAuthDAO.getUserAuth(key); + if (userAuth == null || !userAuth.getUsername().equals(username)) { + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); + } try { - User user = UserDAO.getUser(name); + User user = UserDAO.getUser(username); if (user != null) { - return new ResponseEntity<>(user, HttpStatus.OK); + return new ResponseEntity<>(user.withoutPasswordHash(), HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } @@ -89,7 +93,13 @@ public class UserController { * INTERNAL_SERVER_ERROR if there was an issue */ @PutMapping("/{name}") - public ResponseEntity updateUser(@RequestBody User user, @PathVariable String name) { + public ResponseEntity updateUser(@RequestBody User user, @PathVariable String name, @RequestHeader("jelly-api-key") String key) { + + var userAuth = userAuthDAO.getUserAuth(key); + if (userAuth == null || !userAuth.getUsername().equals(user.getUsername())) { + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); + } + try { user = UserDAO.updateUser(user, name); if (user != null) { @@ -106,14 +116,20 @@ public class UserController { /** * Deletes a user with the desired name * - * @param name The name of the user + * @param username The name of the user * @return OK if the user was deleted, NOT_FOUND if the user was not found, or * INTERNAL_SERVER_ERROR if an error occurred */ - @DeleteMapping("/{name}") - public ResponseEntity deleteUser(@PathVariable String name) { + @DeleteMapping("/{username}") + public ResponseEntity deleteUser(@PathVariable String username, @RequestHeader("jelly-api-key") String key) { + + var userAuth = userAuthDAO.getUserAuth(key); + if (userAuth == null || !userAuth.getUsername().equals(username)) { + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); + } + try { - if (UserDAO.deleteUser(name)) { + if (UserDAO.deleteUser(username)) { return new ResponseEntity<>(HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/model/User.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/model/User.java index 59f4c46..1e182a6 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/model/User.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/model/User.java @@ -7,8 +7,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; public class User { - @JsonProperty("name") - private final String name; + @JsonProperty("username") + private final String username; @JsonProperty("passwordHash") private int passwordHash; @JsonProperty("basket") @@ -17,36 +17,35 @@ public class User { /** * Create a new user * - * @param name The name of the user + * @param username The name of the user */ - public User(String name) { - this.name = name; + public User(String username) { + this.username = username; basket = new ArrayList<>(); } /** * Create a new user * - * @param name The name of the user + * @param username The name of the user * @param basket A basket to copy from */ - public User(@JsonProperty("name") String name, @JsonProperty("basket") List basket) { - this.name = name; + public User(@JsonProperty("username") String username, @JsonProperty("passwordHash") int passwordHash, @JsonProperty("basket") List basket) { + this.username = username; this.basket = basket; + this.passwordHash = passwordHash; } - /** - * Create a deep copy of another user - * - * @param other The user to copy from - */ - public User(User other) { - this.name = other.name; - this.basket = other.basket; + public static User create(String username, String password) { + return new User( + username, + password.hashCode(), + new ArrayList<>() + ); } - public String getName() { - return name; + public String getUsername() { + return username; } public boolean verifyPassword(String password) { @@ -65,4 +64,8 @@ public class User { basket.remove(need); } + public User withoutPasswordHash() { + return new User(this.username, 0, this.basket); + } + } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/model/UserAuth.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/model/UserAuth.java new file mode 100644 index 0000000..1c11a28 --- /dev/null +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/model/UserAuth.java @@ -0,0 +1,43 @@ +package com.ufund.api.ufundapi.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.time.LocalDateTime; +import java.util.UUID; + +public class UserAuth { + @JsonProperty("key") String key; + @JsonProperty("username") String username; + @JsonProperty("expiration") LocalDateTime expiration; + + public UserAuth(@JsonProperty("key") String key, @JsonProperty("username") String username, @JsonProperty("expiration") LocalDateTime expiration) { + this.key = key; + this.expiration = expiration; + this.username = username; + } + + /** + * Generate a new user authentication profile + * @param username the username the key will belong to + * @return The new user authentication profile + */ + public static UserAuth generate(String username) { + return new UserAuth( + UUID.randomUUID().toString(), + username, + LocalDateTime.now().plusDays(30) + ); + } + + public String getKey() { + return key; + } + + public String getUsername() { + return username; + } + + public LocalDateTime getExpiration() { + return expiration; + } +} diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthDAO.java new file mode 100644 index 0000000..45515b8 --- /dev/null +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthDAO.java @@ -0,0 +1,23 @@ +package com.ufund.api.ufundapi.persistence; + +import com.ufund.api.ufundapi.model.UserAuth; + +import java.io.IOException; + +public interface UserAuthDAO { + + /** + * Get a user authentication profile + * @param key The auth key + * @return The authentication profile or null if there was none + */ + UserAuth getUserAuth(String key); + + /** + * Add a user authentication profile + * @param userAuth The user auth profile to add + * @return True if it was successful + * @throws IOException On any file writing error + */ + boolean addUserAuth(UserAuth userAuth) throws IOException; +} diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java new file mode 100644 index 0000000..67918cc --- /dev/null +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java @@ -0,0 +1,62 @@ +package com.ufund.api.ufundapi.persistence; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ufund.api.ufundapi.model.UserAuth; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +@Component +public class UserAuthFIleDAO implements UserAuthDAO { + + private final Map userAuthMap; + private final ObjectMapper objectMapper; + private final String filename; + + public UserAuthFIleDAO(ObjectMapper objectMapper, @Value("${authKeys.file}") String filename) throws IOException { + this.userAuthMap = new HashMap<>(); + this.objectMapper = objectMapper; + this.filename = filename; + load(); + } + + private void load() throws IOException { + userAuthMap.clear(); + + UserAuth[] userAuthKeysArray = objectMapper.readValue(new File(filename), UserAuth[].class); + + for (UserAuth userAuth : userAuthKeysArray) { + userAuthMap.put(userAuth.getKey(), userAuth); + } + } + + private void save() throws IOException { + objectMapper.writeValue(new File(filename), userAuthMap.values()); + } + + public UserAuth[] getAuthKeys() { + synchronized (userAuthMap) { + return userAuthMap.values().toArray(UserAuth[]::new); + } + } + + @Override + public UserAuth getUserAuth(String key) { + synchronized (userAuthMap) { + return userAuthMap.get(key); + } + } + + @Override + public boolean addUserAuth(UserAuth userAuth) throws IOException { + synchronized (userAuthMap) { + userAuthMap.put(userAuth.getKey(), userAuth); + save(); + return true; + } + } +} diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java index d456abc..6558ce2 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java @@ -21,17 +21,17 @@ public interface UserDAO { User[] getUsers() throws IOException; /** - * Retrieves a {@linkplain User user} with the given name + * Retrieves a {@linkplain User user} with the given username * - * @param id The ID of the {@link User user} to get + * @param username The ID of the {@link User user} to get * - * @return a {@link User user} object with the matching name + * @return a {@link User user} object with the matching username *
- * null if no {@link User user} with a matching name is found + * null if no {@link User user} with a matching username is found * * @throws IOException if an issue with underlying storage */ - User getUser(String name) throws IOException; + User getUser(String username) throws IOException; /** * Creates and saves a {@linkplain User user} @@ -44,7 +44,7 @@ public interface UserDAO { * * @throws IOException if an issue with underlying storage */ - User createUser(User user) throws IOException; + User addUser(User user) throws IOException; /** * Updates and saves a {@linkplain User user} @@ -62,7 +62,7 @@ public interface UserDAO { /** * Deletes a {@linkplain User user} with the given id * - * @param id The id of the {@link User user} + * @param username The id of the {@link User user} * * @return true if the {@link User user} was deleted *
@@ -70,5 +70,5 @@ public interface UserDAO { * * @throws IOException if underlying storage cannot be accessed */ - boolean deleteUser(String name) throws IOException; + boolean deleteUser(String username) throws IOException; } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java index 18eec18..54ce74a 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java @@ -36,7 +36,7 @@ public class UserFileDAO implements UserDAO { User[] usersArray = objectMapper.readValue(new File(filename), User[].class); for (User user : usersArray) { - users.put(user.getName(), user); + users.put(user.getUsername(), user); } } @@ -72,15 +72,15 @@ public class UserFileDAO implements UserDAO { /** * Return the user with the String name name or null otherwise * - * @param name Name of desired user + * @param username Name of desired user * * @return Desired user, null otherwise * @throws IOException If there was an IO issue saving the file */ @Override - public User getUser(String name) throws IOException { + public User getUser(String username) throws IOException { synchronized (users) { - return users.getOrDefault(name, null); + return users.getOrDefault(username, null); } } @@ -93,16 +93,11 @@ public class UserFileDAO implements UserDAO { * @throws IOException If there was an IO issue saving the file */ @Override - public User createUser(User user) throws IOException { + public User addUser(User user) throws IOException { synchronized (users) { - if (getUser(user.getName()) == null) { - User newUser = new User(user); - users.put(newUser.getName(), newUser); - save(); - return newUser; - } else { - return null; - } + var res = users.putIfAbsent(user.getUsername(), user); + save(); + return res; } } @@ -131,16 +126,16 @@ public class UserFileDAO implements UserDAO { /** * Delete a user matching the name * - * @param name The name of the user + * @param username The name of the user * * @return True if deleted, false otherwise * @throws IOException If there was an IO issue saving the file */ @Override - public boolean deleteUser(String name) throws IOException { + public boolean deleteUser(String username) throws IOException { synchronized (users) { - if (users.containsKey(name)) { - users.remove(name); + if (users.containsKey(username)) { + users.remove(username); return save(); } else { return false; diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java index 681f47c..496c68c 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java @@ -84,7 +84,7 @@ public class UserControllerTest { User user = new User(username); // when createUser is called, return true simulating successful // creation and save - when(mockUserDAO.createUser(user)).thenReturn(user); + when(mockUserDAO.addUser(user)).thenReturn(user); // Invoke ResponseEntity response = userController.createUser(user); @@ -101,7 +101,7 @@ public class UserControllerTest { User user = new User(username); // when createUser is called, return false simulating failed // creation and save - when(mockUserDAO.createUser(user)).thenReturn(null); + when(mockUserDAO.addUser(user)).thenReturn(null); // Invoke ResponseEntity response = userController.createUser(user); @@ -117,7 +117,7 @@ public class UserControllerTest { User user = new User(username); // When createUser is called on the Mock User DAO, throw an IOException - doThrow(new IOException()).when(mockUserDAO).createUser(user); + doThrow(new IOException()).when(mockUserDAO).addUser(user); // Invoke ResponseEntity response = userController.createUser(user); diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java index 8b8dd99..8cc0900 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java @@ -26,7 +26,7 @@ public class UserTest { User user = new User(expectedName); - assertEquals(expectedName, user.getName()); + assertEquals(expectedName, user.getUsername()); } diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java index dfe9b10..52a1fdc 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java @@ -50,7 +50,7 @@ public class UserFileDAOTest { for (int i = 0; i < testUsers.length;++i) { boolean isInArray = false; for (User user : testUsers) { - if (users[i].getName().equals(user.getName())) { + if (users[i].getUsername().equals(user.getUsername())) { isInArray = true; } } @@ -77,12 +77,12 @@ public class UserFileDAOTest { @Test public void CreateUserTest() throws IOException { User newUser = new User("keshey"); - userFileDAO.createUser(newUser); + userFileDAO.addUser(newUser); User actualUser = userFileDAO.getUser("keshey"); assertNotNull(actualUser); - assertEquals(actualUser.getName(), newUser.getName()); + assertEquals(actualUser.getUsername(), newUser.getUsername()); } @Test @@ -106,7 +106,7 @@ public class UserFileDAOTest { updatedUser = userFileDAO.updateUser(updatedUser, "admin"); assertNotEquals(toBeUpdatedUser, updatedUser); - assertEquals("jellinadmin", updatedUser.getName()); + assertEquals("jellinadmin", updatedUser.getUsername()); } } diff --git a/ufund-ui/src/app/app.component.html b/ufund-ui/src/app/app.component.html index cfebc2b..6b9338c 100644 --- a/ufund-ui/src/app/app.component.html +++ b/ufund-ui/src/app/app.component.html @@ -1,4 +1,6 @@ -

jelly solutions:

+

jelly solutions

+{{currentUser$ | async}} +
diff --git a/ufund-ui/src/app/app.component.ts b/ufund-ui/src/app/app.component.ts index 2dbf33c..a85d04b 100644 --- a/ufund-ui/src/app/app.component.ts +++ b/ufund-ui/src/app/app.component.ts @@ -1,4 +1,7 @@ -import { Component } from '@angular/core'; +import {Component, OnInit} from '@angular/core'; +import {UsersService} from './services/users.service'; +import {BehaviorSubject, Observable, Subject} from 'rxjs'; +import {User} from './models/User'; @Component({ selector: 'app-root', @@ -6,6 +9,18 @@ import { Component } from '@angular/core'; standalone: false, styleUrl: './app.component.css' }) -export class AppComponent { - title = 'ufund-ui'; +export class AppComponent implements OnInit { + // title = 'ufund-ui'; + currentUser$: BehaviorSubject = new BehaviorSubject("Logged out."); + + constructor( + private userService: UsersService + ) {} + + ngOnInit() { + this.userService.getCurrentUser().subscribe(r => { + this.currentUser$?.next("Logged in as " + r.username) + }) + } + } diff --git a/ufund-ui/src/app/components/login/login.component.html b/ufund-ui/src/app/components/login/login.component.html index 41427ae..178ddbf 100644 --- a/ufund-ui/src/app/components/login/login.component.html +++ b/ufund-ui/src/app/components/login/login.component.html @@ -1,5 +1,5 @@

Login:

- - - - + + + + diff --git a/ufund-ui/src/app/components/login/login.component.ts b/ufund-ui/src/app/components/login/login.component.ts index efb8a58..9a4eb0f 100644 --- a/ufund-ui/src/app/components/login/login.component.ts +++ b/ufund-ui/src/app/components/login/login.component.ts @@ -1,4 +1,5 @@ -import { Component } from '@angular/core'; +import { Component } from '@angular/core' +import {UsersService} from '../../services/users.service'; @Component({ selector: 'app-login', @@ -7,5 +8,16 @@ import { Component } from '@angular/core'; styleUrl: './login.component.css' }) export class LoginComponent { + constructor( + protected usersService: UsersService + ) {} + login(username: string | null, password: string | null) { + console.log(`attempting to log in with ${username} ${password}`) + if (!username || !password) { + return; + } + + this.usersService.login(username, password) + } } diff --git a/ufund-ui/src/app/models/Need.ts b/ufund-ui/src/app/models/Need.ts index c0425ec..9e97fd4 100644 --- a/ufund-ui/src/app/models/Need.ts +++ b/ufund-ui/src/app/models/Need.ts @@ -1,7 +1,7 @@ export interface Need { name: string, id: number, - filterAttributes: String[], + filterAttributes: string[], type: GoalType; maxGoal: number; current: number; diff --git a/ufund-ui/src/app/models/User.ts b/ufund-ui/src/app/models/User.ts index 46fe4a1..9149fe7 100644 --- a/ufund-ui/src/app/models/User.ts +++ b/ufund-ui/src/app/models/User.ts @@ -2,5 +2,5 @@ import {Need} from './Need'; export interface User { username: string; - cupboard: Need[]; + basket: Need[]; } diff --git a/ufund-ui/src/app/services/cupboard.service.ts b/ufund-ui/src/app/services/cupboard.service.ts index c123841..4a2b4b0 100644 --- a/ufund-ui/src/app/services/cupboard.service.ts +++ b/ufund-ui/src/app/services/cupboard.service.ts @@ -18,8 +18,7 @@ export class CupboardService { ) {} createNeed(need: Need): Observable { - return this.http.post( - this.url, need, this.httpOptions) + return this.http.post(this.url, need, this.httpOptions) } getNeeds(): Observable { diff --git a/ufund-ui/src/app/services/users.service.ts b/ufund-ui/src/app/services/users.service.ts index 571c004..28cc266 100644 --- a/ufund-ui/src/app/services/users.service.ts +++ b/ufund-ui/src/app/services/users.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import {HttpClient, HttpHeaders} from '@angular/common/http'; -import {Observable} from 'rxjs'; +import {firstValueFrom, Observable, of, Subject} from 'rxjs'; import {User} from '../models/User'; @Injectable({ @@ -8,11 +8,24 @@ import {User} from '../models/User'; }) export class UsersService { - private currentUserID? : number + private currentUser : Subject = new Subject(); + private apiKey: string = ""; private url = "http://localhost:8080/users" + private authUrl = "http://localhost:8080/auth" private httpOptions = { - headers: new HttpHeaders({'Content-Type': 'application/json'}) + headers: new HttpHeaders({ + 'Content-Type': 'application/json', + "jelly-api-key": this.apiKey + }) + }; + private httpOptions2 = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json', + "jelly-api-key": this.apiKey + }), + responseType: "text" as "json" // don't ask me how or why this works, bc i have no clue... + // see the relevant angular bug report https://github.com/angular/angular/issues/18586 }; constructor( @@ -23,7 +36,7 @@ export class UsersService { return this.http.post(this.url, data, this.httpOptions) } - getUser(id: number): Observable { + getUser(id: string): Observable { return this.http.get(`${this.url}/${id}`, this.httpOptions) } @@ -35,7 +48,22 @@ export class UsersService { return this.http.delete(`${this.url}/${id}`, this.httpOptions) } - getCurrentUser(): Observable | undefined { - return this.currentUserID ? this.getUser(this.currentUserID) : undefined + getCurrentUser() { + return this.currentUser; + } + + async login(username: string, password: string) { + let res = this.http.post(this.authUrl, {username: username, password: password}, this.httpOptions2); + this.apiKey = await firstValueFrom(res); + console.log("apikey: "+this.apiKey) + let res2 = this.http.get(`${this.url}/${username}`, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json', + "jelly-api-key": this.apiKey + }) + }) + let currentU = await firstValueFrom(res2); + this.currentUser.next(currentU); + // this.currentUser.subscribe(r => console.log("currentUser: "+r.username)) } } -- cgit v1.2.3 From d752a75a8496f0c5abe3d8ee41af95ae5b7875c3 Mon Sep 17 00:00:00 2001 From: Tyler Ferrari <69283684+Sowgro@users.noreply.github.com> Date: Sun, 2 Mar 2025 11:48:10 -0500 Subject: Configure json parser to only serialize annotated fields --- ufund-api/src/main/resources/application.properties | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ufund-api/src/main/resources/application.properties b/ufund-api/src/main/resources/application.properties index 254ac64..fa5d084 100644 --- a/ufund-api/src/main/resources/application.properties +++ b/ufund-api/src/main/resources/application.properties @@ -2,4 +2,10 @@ server.error.include-message=always cupboard.file=ufund-api/data/cupboard.json -users.file=ufund-api/data/users.json \ No newline at end of file +users.file=ufund-api/data/users.json + +spring.jackson.mapper.auto-detect-getters=false +spring.jackson.mapper.auto-detect-setters=false +spring.jackson.mapper.auto-detect-is-getters=false +spring.jackson.mapper.auto-detect-creators=false +spring.jackson.mapper.auto-detect-fields=false \ No newline at end of file -- cgit v1.2.3 From b3487d216b576a2b2614674eae0a1be139eb1ea3 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 6 Mar 2025 08:16:42 -0500 Subject: Fixed some UserController tests --- .../ufundapi/controller/UserControllerTest.java | 24 ++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java index 496c68c..d189836 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java @@ -1,6 +1,7 @@ package com.ufund.api.ufundapi.controller; import java.io.IOException; +import java.util.HashMap; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.BeforeEach; @@ -13,18 +14,21 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import com.ufund.api.ufundapi.model.User; +import com.ufund.api.ufundapi.model.UserAuth; +import com.ufund.api.ufundapi.persistence.UserAuthFIleDAO; import com.ufund.api.ufundapi.persistence.UserFileDAO; @Tag("Controller-tier") public class UserControllerTest { private UserController userController; private UserFileDAO mockUserDAO; + private UserAuthFIleDAO mockAuthUserDAO; @BeforeEach public void setupUserController() { mockUserDAO = mock(UserFileDAO.class); - userController = new UserController(mockUserDAO); - + mockAuthUserDAO = mock(UserAuthFIleDAO.class); + userController = new UserController(mockUserDAO, mockAuthUserDAO); } @Test @@ -32,11 +36,13 @@ public class UserControllerTest { // Setup String username = "Test"; User user = new User(username); + String key = UserAuth.generate(username).getKey(); // When the same id is passed in, our mock User DAO will return the User object when(mockUserDAO.getUser(username)).thenReturn(user); + // Invoke - ResponseEntity response = userController.getUser(username); + ResponseEntity response = userController.getUser(username, key); // Analyze assertEquals(HttpStatus.OK, response.getStatusCode()); @@ -47,12 +53,14 @@ public class UserControllerTest { public void testGetUserNotFound() throws Exception { // createUser may throw IOException // Setup String username = "Test"; + String key = UserAuth.generate(username).getKey(); // When the same id is passed in, our mock User DAO will return null, simulating // no User found when(mockUserDAO.getUser(username)).thenReturn(null); + // Invoke - ResponseEntity response = userController.getUser(username); + ResponseEntity response = userController.getUser(username, key); // Analyze assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); @@ -62,11 +70,12 @@ public class UserControllerTest { public void testGetUserHandleException() throws Exception { // createUser may throw IOException // Setup String username = "Test"; + String key = UserAuth.generate(username).getKey(); // When getUser is called on the Mock User DAO, throw an IOException doThrow(new IOException()).when(mockUserDAO).getUser(username); // Invoke - ResponseEntity response = userController.getUser(username); + ResponseEntity response = userController.getUser(username, key); // Analyze assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); @@ -82,12 +91,15 @@ public class UserControllerTest { // Setup String username = "Test"; User user = new User(username); + String key = UserAuth.generate(username).getKey(); // when createUser is called, return true simulating successful // creation and save when(mockUserDAO.addUser(user)).thenReturn(user); + + // Invoke - ResponseEntity response = userController.createUser(user); + ResponseEntity response = userController.createUser(params); // Analyze assertEquals(HttpStatus.CREATED, response.getStatusCode()); -- cgit v1.2.3 From 3f015edcf2d03da4b1bbd9c81bbcb3a914428140 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 6 Mar 2025 08:18:24 -0500 Subject: Fixed user tests --- ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java index 8cc0900..6f35df0 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java @@ -23,8 +23,9 @@ public class UserTest { public void testUsername() { String expectedName = "Bob"; + String password = "password"; - User user = new User(expectedName); + User user = User.create(expectedName, password); assertEquals(expectedName, user.getUsername()); -- cgit v1.2.3 From 4cfacd63b1552bf6ea33e28f3f66e11b75e5756a Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 6 Mar 2025 12:45:35 -0500 Subject: Created Cupboard Service and refactored the controller and DAO to add the service as an inbetween with logic --- .../ufundapi/controller/CupboardController.java | 55 ++++++++------ .../java/com/ufund/api/ufundapi/model/Need.java | 13 ++++ .../api/ufundapi/persistence/CupboardDAO.java | 17 +---- .../api/ufundapi/persistence/CupboardFileDao.java | 21 +----- .../api/ufundapi/service/CupboardService.java | 83 ++++++++++++++++++++++ 5 files changed, 133 insertions(+), 56 deletions(-) create mode 100644 ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java index 4b2a04d..6b0bb71 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java @@ -1,6 +1,7 @@ package com.ufund.api.ufundapi.controller; import java.io.IOException; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -17,21 +18,23 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.ufund.api.ufundapi.model.Need; -import com.ufund.api.ufundapi.persistence.CupboardDAO; +import com.ufund.api.ufundapi.model.Need.GoalType; +import com.ufund.api.ufundapi.service.CupboardService; +import com.ufund.api.ufundapi.service.CupboardService.DuplicateKeyException; @RestController @RequestMapping("cupboard") public class CupboardController { private static final Logger LOG = Logger.getLogger(CupboardController.class.getName()); - private final CupboardDAO cupboardDAO; + private final CupboardService cupboardService; /** * Create a cupboard controller to receive REST signals * - * @param cupboardDAO The Data Access Object + * @param cupboardService The Data Access Object */ - public CupboardController(CupboardDAO cupboardDAO) { - this.cupboardDAO = cupboardDAO; + public CupboardController(CupboardService cupboardService) { + this.cupboardService = cupboardService; } /** @@ -41,16 +44,20 @@ public class CupboardController { * @return OK response and the need if it was successful, INTERNAL_SERVER_ERROR otherwise */ @PostMapping("") - public ResponseEntity createNeed(@RequestBody Need need) { + public ResponseEntity createNeed(@RequestBody Map params) { + String name = params.get("name"); + int maxGoal = Integer.parseInt(params.get("maxGoal")); + Need.GoalType goalType = GoalType.valueOf(params.get("maxGoal")); + try { - if (need.getMaxGoal() <= 0) { - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); - } - if (need.getMaxGoal() < need.getCurrent()) { - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); - } - cupboardDAO.createNeed(need); + + Need need = cupboardService.createNeed(name, maxGoal, goalType); return new ResponseEntity<>(need, HttpStatus.OK); + + } catch (DuplicateKeyException ex) { + return new ResponseEntity<>(HttpStatus.CONFLICT); + } catch (IllegalArgumentException ex) { + return new ResponseEntity<>(HttpStatus.UNPROCESSABLE_ENTITY); } catch (IOException ex) { return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } @@ -69,7 +76,7 @@ public class CupboardController { LOG.info("GET /needs"); try { - Need[] needs = cupboardDAO.getNeeds(); + Need[] needs = cupboardService.getNeeds(); return new ResponseEntity<>(needs, HttpStatus.OK); } catch (IOException e) { LOG.log(Level.SEVERE, e.getLocalizedMessage()); @@ -93,8 +100,8 @@ public class CupboardController { LOG.info("GET /need/?name="+name); try { - Need[] needArray = cupboardDAO.findNeeds(name); - return new ResponseEntity<>(needArray, HttpStatus.OK); + Need[] needs = cupboardService.searchNeeds(name); + return new ResponseEntity<>(needs, HttpStatus.OK); } catch (IOException e) { LOG.log(Level.SEVERE,e.getLocalizedMessage()); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); @@ -116,7 +123,7 @@ public class CupboardController { LOG.log(Level.INFO, "GET /need/{0}", id); try { - Need need = cupboardDAO.getNeed(id); + Need need = cupboardService.getNeed(id); if (need != null) { return new ResponseEntity<>(need, HttpStatus.OK); } else { @@ -140,8 +147,12 @@ public class CupboardController { @PutMapping("") public ResponseEntity updateNeed(@RequestBody Need need) { try { - need = cupboardDAO.updateNeed(need); - return new ResponseEntity<>(need, HttpStatus.OK); + Need updatedNeed = cupboardService.updateNeed(need); + if (updatedNeed != null) { + return new ResponseEntity<>(need, HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } } catch (IOException e) { return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } @@ -156,9 +167,9 @@ public class CupboardController { @DeleteMapping("/{id}") public ResponseEntity deleteNeed(@PathVariable int id) { try { - if (cupboardDAO.getNeed(id) != null) { - cupboardDAO.deleteNeed(id); - return new ResponseEntity<>(HttpStatus.OK); + Need need = cupboardService.getNeed(id); + if (cupboardService.deleteNeed(id)) { + return new ResponseEntity<>(need, HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/model/Need.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/model/Need.java index 2611357..9ca097a 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/model/Need.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/model/Need.java @@ -31,6 +31,19 @@ public class Need { this.type = type; } + /** + * Create a new need + * + * @param name The name of the need + * @param maxGoal The maximum goal for this need + * @param type The type of need (monetary, physical) + */ + public Need(String name, GoalType type, double maxGoal) { + this.name = name; + this.type = type; + this.maxGoal = maxGoal; + } + /** * Create a deep copy of another need * diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardDAO.java index 1435410..6baf3e4 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardDAO.java @@ -1,9 +1,9 @@ package com.ufund.api.ufundapi.persistence; -import com.ufund.api.ufundapi.model.Need; - import java.io.IOException; +import com.ufund.api.ufundapi.model.Need; + /** * Defines the interface for Need object persistence * @@ -19,17 +19,6 @@ public interface CupboardDAO { */ Need[] getNeeds() throws IOException; - /** - * Finds all {@linkplain Need needs} whose name contains the given text - * - * @param targetName The text to match against - * - * @return An array of {@link Need needs} whose names contains the given text, may be empty - * - * @throws IOException if an issue with underlying storage - */ - Need[] findNeeds(String targetName) throws IOException; - /** * Retrieves a {@linkplain Need need} with the given name * @@ -54,7 +43,7 @@ public interface CupboardDAO { * * @throws IOException if an issue with underlying storage */ - Need createNeed(Need need) throws IOException; + Need addNeed(Need need) throws IOException; /** * Updates and saves a {@linkplain Need need} diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDao.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDao.java index 81ee7c0..84ea693 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDao.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDao.java @@ -60,18 +60,6 @@ public class CupboardFileDao implements CupboardDAO { return needs.values().toArray(Need[]::new); } - /** - * Returns an array of needs filtered by a search - * - * @param search The search substring - * @return The requested array - */ - private Need[] getNeedsArray(String search) { - return needs.values().stream() - .filter(i -> i.getName().toLowerCase().contains(search.toLowerCase())) - .toArray(Need[]::new); - } - /** * Saves the needs to json * @@ -92,13 +80,6 @@ public class CupboardFileDao implements CupboardDAO { } } - @Override - public Need[] findNeeds(String targetName) { - synchronized (needs) { - return getNeedsArray(targetName); - } - } - @Override public Need getNeed(int id) { synchronized (needs) { @@ -107,7 +88,7 @@ public class CupboardFileDao implements CupboardDAO { } @Override - public Need createNeed(Need need) throws IOException { + public Need addNeed(Need need) throws IOException { synchronized (needs) { Need newNeed = new Need(need); newNeed.setID(nextId()); diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java new file mode 100644 index 0000000..860a2a8 --- /dev/null +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java @@ -0,0 +1,83 @@ +package com.ufund.api.ufundapi.service; + +import java.io.IOException; +import java.util.Arrays; + +import com.ufund.api.ufundapi.model.Need; +import com.ufund.api.ufundapi.persistence.CupboardDAO; + +public class CupboardService { + + private final CupboardDAO cupboardDAO; + + public class DuplicateKeyException extends Exception { + + public DuplicateKeyException(String message) { + super(message); + } + + } + + public CupboardService(CupboardDAO cupboardDAO) { + this.cupboardDAO = cupboardDAO; + } + + public Need createNeed(String name, int maxGoal, Need.GoalType goalType) throws IOException, DuplicateKeyException { + + Need need = new Need(name, goalType, maxGoal); + + if (need.getMaxGoal() <= 0) { + throw new IllegalArgumentException("Max Goal must be greater than zero"); + } else { + for (Need searchNeed : cupboardDAO.getNeeds()) { + if (need.getName().equalsIgnoreCase(searchNeed.getName())) { + throw new DuplicateKeyException("Duplicate names are not allowed"); + } + } + return cupboardDAO.addNeed(need); + } + + } + + public Need[] getNeeds() throws IOException { + return cupboardDAO.getNeeds(); + } + + /** + * Returns an array of needs filtered by a search + * + * @param search The search substring + * @return The requested array + * @throws IOException + */ + public Need[] searchNeeds(String search) throws IOException { + return Arrays.stream(cupboardDAO.getNeeds()) + .filter(i -> i.getName().toLowerCase().contains(search.toLowerCase())) + .toArray(Need[]::new); + } + + /** + * @param id + * @return + * @throws IOException + */ + public Need getNeed(int id) throws IOException { + return cupboardDAO.getNeed(id); + } + + /** + * + * @param need + * @return + * @throws IOException + */ + public Need updateNeed(Need need) throws IOException { + return cupboardDAO.updateNeed(need); + } + + public boolean deleteNeed(int id) throws IOException { + return cupboardDAO.deleteNeed(id); + } + + +} -- cgit v1.2.3 From 7cfa986e9c46f16c08fb490f3af9717a20488a1f Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 6 Mar 2025 12:45:48 -0500 Subject: Created Auth service class --- .../src/main/java/com/ufund/api/ufundapi/service/AuthService.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java new file mode 100644 index 0000000..caf1edd --- /dev/null +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java @@ -0,0 +1,5 @@ +package com.ufund.api.ufundapi.service; + +public class AuthService { + +} -- cgit v1.2.3 From d2539df788d97e23dedd06cf42eca92c4aa08112 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 6 Mar 2025 12:45:53 -0500 Subject: Created user service class --- .../src/main/java/com/ufund/api/ufundapi/service/UserService.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java new file mode 100644 index 0000000..994512d --- /dev/null +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java @@ -0,0 +1,5 @@ +package com.ufund.api.ufundapi.service; + +public class UserService { + +} -- cgit v1.2.3 From e9d5addc7a0b65c426803171471ca5a042b73c93 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 6 Mar 2025 17:24:15 -0500 Subject: Migrated auth controller methods to auth service --- .../api/ufundapi/controller/AuthController.java | 24 +++++++------- .../ufund/api/ufundapi/service/AuthService.java | 38 +++++++++++++++++++++- 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java index aa27e3f..b9c8ed3 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java @@ -3,22 +3,25 @@ package com.ufund.api.ufundapi.controller; import com.ufund.api.ufundapi.model.UserAuth; import com.ufund.api.ufundapi.persistence.UserAuthDAO; import com.ufund.api.ufundapi.persistence.UserDAO; +import com.ufund.api.ufundapi.service.AuthService; +import com.ufund.api.ufundapi.service.UserService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import javax.net.ssl.HttpsURLConnection; import java.io.IOException; import java.util.Map; @RestController @RequestMapping("auth") public class AuthController { - private final UserDAO userDAO; - private final UserAuthDAO userAuthDAO; + private final UserService userService; + private final AuthService authService; - public AuthController(UserDAO userDAO, UserAuthDAO userAuthDAO) { - this.userDAO = userDAO; - this.userAuthDAO = userAuthDAO; + public AuthController(UserService userService, AuthService authService) { + this.userService = userService; + this.authService = authService; } /** @@ -31,15 +34,12 @@ public class AuthController { String username = params.get("username"); String password = params.get("password"); try { - var usr = userDAO.getUser(username); - if (usr == null || !usr.verifyPassword(password)) { - return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); - } - var userAuth = UserAuth.generate(username); - userAuthDAO.addUserAuth(userAuth); - return new ResponseEntity<>(userAuth.getKey(), HttpStatus.OK); + String key = authService.login(username, password); + return new ResponseEntity<>(key, HttpStatus.OK); } catch (IOException ex) { return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (IllegalAccessException e) { + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); } } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java index caf1edd..2e644ee 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java @@ -1,5 +1,41 @@ package com.ufund.api.ufundapi.service; +import com.ufund.api.ufundapi.model.UserAuth; +import com.ufund.api.ufundapi.persistence.UserAuthDAO; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component public class AuthService { - + + private final UserAuthDAO userAuthDAO; + private final UserService userService; + + public AuthService(UserAuthDAO userAuthDAO, UserService userService) { + this.userAuthDAO = userAuthDAO; + this.userService = userService; + } + + public UserAuth getUserAuth(String key) { + return userAuthDAO.getUserAuth(key); + } + + public void authenticate(String username, String key) throws IllegalAccessException { + var userAuth = getUserAuth(key); + if (userAuth == null || !userAuth.getUsername().equals(username)) { + throw new IllegalAccessException("Unauthorized"); + } + } + + public String login(String username, String password) throws IllegalAccessException, IOException { + var usr = userService.getUser(username); + if (usr == null || !usr.verifyPassword(password)) { + throw new IllegalAccessException("Unauthorized"); + } + var userAuth = UserAuth.generate(username); + userAuthDAO.addUserAuth(userAuth); + return userAuth.getKey(); + } + } -- cgit v1.2.3 From 42c61d799bb5828949d71dfce6b83dccd3514768 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 6 Mar 2025 17:24:47 -0500 Subject: Migrated user controller methods to user service. Also changed some return types. --- .../api/ufundapi/controller/UserController.java | 65 ++++++++++------------ .../api/ufundapi/persistence/UserFileDAO.java | 5 ++ .../ufund/api/ufundapi/service/UserService.java | 38 +++++++++++++ 3 files changed, 73 insertions(+), 35 deletions(-) diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java index aa9598d..02526af 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java @@ -5,29 +5,30 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; -import com.ufund.api.ufundapi.persistence.UserAuthDAO; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import com.ufund.api.ufundapi.model.User; -import com.ufund.api.ufundapi.persistence.UserDAO; +import com.ufund.api.ufundapi.service.AuthService; +import com.ufund.api.ufundapi.service.UserService; @RestController @RequestMapping("users") public class UserController { private static final Logger LOG = Logger.getLogger(UserController.class.getName()); - private final UserDAO UserDAO; - private final UserAuthDAO userAuthDAO; + private final UserService userService; + private final AuthService authService; /** - * Create a user controller to receive REST signals - * - * @param userDAO The Data Access Object + * Creates a UserController + * + * @param userService + * @param authService */ - public UserController(UserDAO userDAO, UserAuthDAO userAuthDAO) { - this.UserDAO = userDAO; - this.userAuthDAO = userAuthDAO; + public UserController(UserService userService, AuthService authService) { + this.userService = userService; + this.authService = authService; } /** @@ -37,13 +38,14 @@ public class UserController { * otherwise */ @PostMapping("") - public ResponseEntity createUser(@RequestBody Map params) { + public ResponseEntity createUser(@RequestBody Map params) { String username = params.get("username"); String password = params.get("password"); try { - if (UserDAO.addUser(User.create(username, password)) != null) { - return new ResponseEntity<>(true, HttpStatus.CREATED); + User user = userService.createUser(username, password); + if (user == null) { + return new ResponseEntity<>(user, HttpStatus.CREATED); } else { return new ResponseEntity<>(HttpStatus.CONFLICT); } @@ -65,19 +67,16 @@ public class UserController { public ResponseEntity getUser(@PathVariable String username, @RequestHeader("jelly-api-key") String key) { LOG.log(Level.INFO, "GET /user/{0}", username); - var userAuth = userAuthDAO.getUserAuth(key); - if (userAuth == null || !userAuth.getUsername().equals(username)) { - return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); - } - try { - User user = UserDAO.getUser(username); + authService.authenticate(username, key); + User user = userService.getUser(username); if (user != null) { return new ResponseEntity<>(user.withoutPasswordHash(), HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } - + } catch (IllegalAccessException ex) { + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); } catch (IOException e) { LOG.log(Level.SEVERE, e.getLocalizedMessage()); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); @@ -92,16 +91,12 @@ public class UserController { * @return OK response and the user if it was successful, or * INTERNAL_SERVER_ERROR if there was an issue */ - @PutMapping("/{name}") - public ResponseEntity updateUser(@RequestBody User user, @PathVariable String name, @RequestHeader("jelly-api-key") String key) { - - var userAuth = userAuthDAO.getUserAuth(key); - if (userAuth == null || !userAuth.getUsername().equals(user.getUsername())) { - return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); - } + @PutMapping("/{username}") + public ResponseEntity updateUser(@RequestBody User user, @PathVariable String username, @RequestHeader("jelly-api-key") String key) { try { - user = UserDAO.updateUser(user, name); + authService.authenticate(username, key); + user = userService.updateUser(user, username); if (user != null) { return new ResponseEntity<>(user, HttpStatus.OK); } else { @@ -110,6 +105,8 @@ public class UserController { } catch (IOException e) { return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (IllegalAccessException e) { + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); } } @@ -121,21 +118,19 @@ public class UserController { * INTERNAL_SERVER_ERROR if an error occurred */ @DeleteMapping("/{username}") - public ResponseEntity deleteUser(@PathVariable String username, @RequestHeader("jelly-api-key") String key) { - - var userAuth = userAuthDAO.getUserAuth(key); - if (userAuth == null || !userAuth.getUsername().equals(username)) { - return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); - } + public ResponseEntity deleteUser(@PathVariable String username, @RequestHeader("jelly-api-key") String key) { try { - if (UserDAO.deleteUser(username)) { + authService.authenticate(username, key); + if (userService.deleteUser(username)) { return new ResponseEntity<>(HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } } catch (IOException e) { return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (IllegalAccessException e) { + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); } } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java index 54ce74a..4f43f8c 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java @@ -97,6 +97,11 @@ public class UserFileDAO implements UserDAO { synchronized (users) { var res = users.putIfAbsent(user.getUsername(), user); save(); + if (res == null) { + return user; + } else { + + } return res; } } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java index 994512d..c23bf89 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java @@ -1,5 +1,43 @@ package com.ufund.api.ufundapi.service; +import java.io.IOException; + +import com.ufund.api.ufundapi.model.User; +import com.ufund.api.ufundapi.persistence.UserAuthDAO; +import com.ufund.api.ufundapi.persistence.UserDAO; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; + +@Component public class UserService { + + private final UserDAO userDAO; + + /** + * Create a user controller to receive REST signals + * + * @param userDao The Data Access Object + */ + public UserService(UserDAO userDao, AuthService authService) { + this.userDAO = userDao; + } + + public User createUser(String username, String password) throws IOException { + User user = User.create(username, password); + return userDAO.addUser(user); + } + + public User getUser(String username) throws IOException, IllegalAccessException { + return userDAO.getUser(username); + } + + public User updateUser(User user, String name) throws IllegalAccessException, IOException { + return userDAO.updateUser(user, name); + } + + public Boolean deleteUser(String username) throws IllegalAccessException, IOException { + return userDAO.deleteUser(username); + } } -- cgit v1.2.3 From 5624b60592969d8f7e7686644b9b682ceeb40362 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 6 Mar 2025 17:25:06 -0500 Subject: Removed extra file extensions --- ufund-api/src/main/resources/application.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ufund-api/src/main/resources/application.properties b/ufund-api/src/main/resources/application.properties index fa5d084..a866f98 100644 --- a/ufund-api/src/main/resources/application.properties +++ b/ufund-api/src/main/resources/application.properties @@ -1,8 +1,8 @@ # rename to application.properties server.error.include-message=always -cupboard.file=ufund-api/data/cupboard.json -users.file=ufund-api/data/users.json +cupboard.file=data/cupboard.json +users.file=data/users.json spring.jackson.mapper.auto-detect-getters=false spring.jackson.mapper.auto-detect-setters=false -- cgit v1.2.3 From 1fe3905e9d4354657d22e9dbc1a244108ab55a83 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 6 Mar 2025 17:27:15 -0500 Subject: Removed unused imports and fixed other warnings --- .../java/com/ufund/api/ufundapi/controller/AuthController.java | 8 +------- .../main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java | 2 -- .../src/main/java/com/ufund/api/ufundapi/service/UserService.java | 3 --- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java index b9c8ed3..1a545f6 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java @@ -1,26 +1,20 @@ package com.ufund.api.ufundapi.controller; -import com.ufund.api.ufundapi.model.UserAuth; -import com.ufund.api.ufundapi.persistence.UserAuthDAO; -import com.ufund.api.ufundapi.persistence.UserDAO; import com.ufund.api.ufundapi.service.AuthService; import com.ufund.api.ufundapi.service.UserService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import javax.net.ssl.HttpsURLConnection; import java.io.IOException; import java.util.Map; @RestController @RequestMapping("auth") public class AuthController { - private final UserService userService; private final AuthService authService; - public AuthController(UserService userService, AuthService authService) { - this.userService = userService; + public AuthController(AuthService authService) { this.authService = authService; } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java index 4f43f8c..dca812b 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java @@ -99,8 +99,6 @@ public class UserFileDAO implements UserDAO { save(); if (res == null) { return user; - } else { - } return res; } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java index c23bf89..a545029 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java @@ -3,10 +3,7 @@ package com.ufund.api.ufundapi.service; import java.io.IOException; import com.ufund.api.ufundapi.model.User; -import com.ufund.api.ufundapi.persistence.UserAuthDAO; import com.ufund.api.ufundapi.persistence.UserDAO; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @Component -- cgit v1.2.3 From bb9ce55cb5b55a6aaed2399e39a01d68f2491ce3 Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 6 Mar 2025 21:41:39 -0500 Subject: Push current changes (working on documentation and tests) --- ufund-api/data/userAuths.json | 8 +- ufund-api/pom.xml | 4 +- .../api/ufundapi/controller/AuthController.java | 25 ++-- .../ufundapi/controller/CupboardController.java | 15 +-- .../api/ufundapi/persistence/CupboardDAO.java | 4 - .../api/ufundapi/persistence/CupboardFileDAO.java | 126 +++++++++++++++++++++ .../api/ufundapi/persistence/CupboardFileDao.java | 126 --------------------- .../api/ufundapi/persistence/UserAuthDAO.java | 17 ++- .../api/ufundapi/persistence/UserAuthFIleDAO.java | 27 +++-- .../api/ufundapi/persistence/UserFileDAO.java | 23 +--- .../ufund/api/ufundapi/service/AuthService.java | 32 +++++- .../api/ufundapi/service/CupboardService.java | 25 ++-- .../ufund/api/ufundapi/service/UserService.java | 2 +- .../src/main/resources/application.properties | 1 + .../controller/CupboardControllerTest.java | 6 +- .../ufundapi/persistence/CupboardFileDAOTest.java | 107 +++++++++++++++++ .../ufundapi/persistence/CupboardFileDaoTest.java | 108 ------------------ ufund-ui/src/app/app.module.ts | 6 + .../components/dashboard/dashboard.component.html | 2 + .../components/dashboard/dashboard.component.ts | 2 +- .../src/app/components/login/login.component.ts | 8 +- 21 files changed, 360 insertions(+), 314 deletions(-) create mode 100644 ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDAO.java delete mode 100644 ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDao.java create mode 100644 ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java delete mode 100644 ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDaoTest.java diff --git a/ufund-api/data/userAuths.json b/ufund-api/data/userAuths.json index e451862..41ff472 100644 --- a/ufund-api/data/userAuths.json +++ b/ufund-api/data/userAuths.json @@ -1,7 +1 @@ -[ - { - "key": "eeea7b02-7265-4a26-96de-a8ad1860c533", - "username": "phil", - "expiration": "2025-03-31T23:04:47.455490668" - } -] \ No newline at end of file +[{"key":"a07ae51f-f80b-4001-95f1-48c11d4917a4","username":"phil","expiration":"2025-04-05T15:04:30.900359001"},{"key":"e14f8ee5-5780-4b9b-bf34-7a41c2bbfcb4","username":"phil","expiration":"2025-04-05T13:46:10.90733016"},{"key":"d7cef571-0f76-49fe-941f-ecbeae69557a","username":"phil","expiration":"2025-04-05T15:14:00.363201102"},{"key":"eeea7b02-7265-4a26-96de-a8ad1860c533","username":"phil","expiration":"2025-03-31T23:04:47.455490668"}] \ No newline at end of file diff --git a/ufund-api/pom.xml b/ufund-api/pom.xml index ce96d60..d874a29 100644 --- a/ufund-api/pom.xml +++ b/ufund-api/pom.xml @@ -73,8 +73,8 @@ jacoco-maven-plugin ${jacoco.version} - /target/coverage-reports/jacoco-unit.exec - /target/coverage-reports/jacoco-unit.exec + /target/coverage-reports/jacoco-unit.exec + /target/coverage-reports/jacoco-unit.exec diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java index 1a545f6..b0390ae 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java @@ -20,8 +20,10 @@ public class AuthController { /** * Attempts to log in as a user - * @param params A map/json object in the format {username: string, password: string} - * @return An api key if the auth was successful, null otherwise + * + * @param params A json object in the format {username: string, password: string} + * @return An api key and status OK if the authentication was successful, + * Status UNAUTHORIZED if the authentication failed and INTERNAL SERVER ERROR otherwise. */ @PostMapping("") public ResponseEntity login(@RequestBody Map params) { @@ -30,19 +32,26 @@ public class AuthController { try { String key = authService.login(username, password); return new ResponseEntity<>(key, HttpStatus.OK); - } catch (IOException ex) { - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } catch (IllegalAccessException e) { return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); + } catch (IOException ex) { + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } /** - * TODO - * @return + * Logs out the current user + * + * @param key The API sent by the client in the header + * @return OK if the user was successfully logged out, INTERNAL_SERVER_ERROR otherwise. */ @DeleteMapping("") - public ResponseEntity logout() { - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + public ResponseEntity logout(@RequestHeader("jelly-api-key") String key) { + try { + authService.logout(key); + return new ResponseEntity<>(HttpStatus.OK); + } catch (IOException e) { + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } } } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java index 6b0bb71..dfcb8a3 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java @@ -40,8 +40,11 @@ public class CupboardController { /** * Creates a Need with the provided object * - * @param need The need to create - * @return OK response and the need if it was successful, INTERNAL_SERVER_ERROR otherwise + * @param params The need to create + * @return OK response and the need if it was successful, + * CONFLICT if another need with the same name exists + * UNPROCESSABLE_ENTITY if the need contains bad data + * INTERNAL_SERVER_ERROR otherwise */ @PostMapping("") public ResponseEntity createNeed(@RequestBody Map params) { @@ -50,10 +53,8 @@ public class CupboardController { Need.GoalType goalType = GoalType.valueOf(params.get("maxGoal")); try { - Need need = cupboardService.createNeed(name, maxGoal, goalType); return new ResponseEntity<>(need, HttpStatus.OK); - } catch (DuplicateKeyException ex) { return new ResponseEntity<>(HttpStatus.CONFLICT); } catch (IllegalArgumentException ex) { @@ -113,10 +114,8 @@ public class CupboardController { * * @param id The id used to locate the {@link Need need} * - * @return ResponseEntity with {@link Need need} object and HTTP status of OK if - * found
+ * @return ResponseEntity with {@link Need need} object and HTTP status of OK if found
* ResponseEntity with HTTP status of NOT_FOUND if not found
- * ResponseEntity with HTTP status of INTERNAL_SERVER_ERROR otherwise */ @GetMapping("/{id}") public ResponseEntity getNeed(@PathVariable int id) { @@ -129,7 +128,6 @@ public class CupboardController { } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } - } catch (IOException e) { LOG.log(Level.SEVERE, e.getLocalizedMessage()); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); @@ -143,7 +141,6 @@ public class CupboardController { * @param need The need to update * @return OK response and the need if it was successful, or INTERNAL_SERVER_ERROR if there was an issue */ - @PutMapping("") public ResponseEntity updateNeed(@RequestBody Need need) { try { diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardDAO.java index 6baf3e4..c8285a0 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardDAO.java @@ -14,8 +14,6 @@ public interface CupboardDAO { * Retrieves all {@linkplain Need needs} * * @return An array of {@link Need need} objects, may be empty - * - * @throws IOException if an issue with underlying storage */ Need[] getNeeds() throws IOException; @@ -27,8 +25,6 @@ public interface CupboardDAO { * @return a {@link Need need} object with the matching name *
* null if no {@link Need need} with a matching name is found - * - * @throws IOException if an issue with underlying storage */ Need getNeed(int id) throws IOException; diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDAO.java new file mode 100644 index 0000000..c4aaca3 --- /dev/null +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDAO.java @@ -0,0 +1,126 @@ +package com.ufund.api.ufundapi.persistence; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ufund.api.ufundapi.model.Need; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.TreeMap; + +@Component +public class CupboardFileDAO implements CupboardDAO { + + private final Map needs; // cache + private final ObjectMapper objectMapper; + private static int nextId; + private final String filename; + + public CupboardFileDAO(@Value("${cupboard.file}") String filename, ObjectMapper objectMapper) throws IOException { + this.filename = filename; + this.objectMapper = objectMapper; + needs = new TreeMap<>(); + load(); // load the heroes from the file + } + + private synchronized static int nextId() { + int id = nextId; + nextId++; + return id; + } + + /** + * Load changes from the json file + * + * @throws IOException Any IO issue with the file + */ + private void load() throws IOException { + needs.clear(); + nextId = 0; + + Need[] needsArray = objectMapper.readValue(new File(filename), Need[].class); + + for (Need need : needsArray) { + needs.put(need.getId(), need); + if (need.getId() > nextId()) { + nextId = need.getId(); + } + } + nextId++; + } + + /** + * Return an array of the needs + * + * @return An array of all the needs + */ + private Need[] getNeedsArray() { + return needs.values().toArray(Need[]::new); + } + + /** + * Saves the needs to json + * + * @return True if the save was successful, false otherwise + * @throws IOException If there was an IO issue saving the file + */ + private boolean save() throws IOException { + Need[] needArray = getNeedsArray(); + + objectMapper.writeValue(new File(filename), needArray); + return true; + } + + @Override + public Need[] getNeeds() { + synchronized (needs) { + return getNeedsArray(); + } + } + + @Override + public Need getNeed(int id) { + synchronized (needs) { + return needs.getOrDefault(id, null); + } + } + + @Override + public Need addNeed(Need need) throws IOException { + synchronized (needs) { + Need newNeed = new Need(need); + newNeed.setID(nextId()); + needs.put(newNeed.getId(), newNeed); + save(); + return newNeed; + } + } + + @Override + public Need updateNeed(Need need) throws IOException { + synchronized (needs) { + if (needs.containsKey(need.getId())) { + needs.put(need.getId(), need); + save(); + return need; + } else { + return null; + } + + } + } + + @Override + public boolean deleteNeed(int id) throws IOException { + synchronized (needs) { + if (needs.containsKey(id)) { + needs.remove(id); + return save(); + } else { + return false; + } + } + } +} \ No newline at end of file diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDao.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDao.java deleted file mode 100644 index 84ea693..0000000 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDao.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.ufund.api.ufundapi.persistence; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.ufund.api.ufundapi.model.Need; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import java.io.File; -import java.io.IOException; -import java.util.Map; -import java.util.TreeMap; - -@Component -public class CupboardFileDao implements CupboardDAO { - - private final Map needs; // cache - private final ObjectMapper objectMapper; - private static int nextId; - private final String filename; - - public CupboardFileDao(@Value("${cupboard.file}") String filename, ObjectMapper objectMapper) throws IOException { - this.filename = filename; - this.objectMapper = objectMapper; - needs = new TreeMap<>(); - load(); // load the heroes from the file - } - - private synchronized static int nextId() { - int id = nextId; - nextId++; - return id; - } - - /** - * Load changes from the json file - * - * @throws IOException Any IO issue with the file - */ - private void load() throws IOException { - needs.clear(); - nextId = 0; - - Need[] needsArray = objectMapper.readValue(new File(filename), Need[].class); - - for (Need need : needsArray) { - needs.put(need.getId(), need); - if (need.getId() > nextId()) { - nextId = need.getId(); - } - } - nextId++; - } - - /** - * Return an array of the needs - * - * @return An array of all the needs - */ - private Need[] getNeedsArray() { - return needs.values().toArray(Need[]::new); - } - - /** - * Saves the needs to json - * - * @return True if the save was successful, false otherwise - * @throws IOException If there was an IO issue saving the file - */ - private boolean save() throws IOException { - Need[] needArray = getNeedsArray(); - - objectMapper.writeValue(new File(filename), needArray); - return true; - } - - @Override - public Need[] getNeeds() { - synchronized (needs) { - return getNeedsArray(); - } - } - - @Override - public Need getNeed(int id) { - synchronized (needs) { - return needs.getOrDefault(id, null); - } - } - - @Override - public Need addNeed(Need need) throws IOException { - synchronized (needs) { - Need newNeed = new Need(need); - newNeed.setID(nextId()); - needs.put(newNeed.getId(), newNeed); - save(); - return newNeed; - } - } - - @Override - public Need updateNeed(Need need) throws IOException { - synchronized (needs) { - if (needs.containsKey(need.getId())) { - needs.put(need.getId(), need); - save(); - return need; - } else { - return null; - } - - } - } - - @Override - public boolean deleteNeed(int id) throws IOException { - synchronized (needs) { - if (needs.containsKey(id)) { - needs.remove(id); - return save(); - } else { - return false; - } - } - } -} \ No newline at end of file diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthDAO.java index 45515b8..355aae4 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthDAO.java @@ -8,16 +8,25 @@ public interface UserAuthDAO { /** * Get a user authentication profile + * * @param key The auth key * @return The authentication profile or null if there was none */ - UserAuth getUserAuth(String key); + UserAuth getUserAuth(String key) throws IOException; /** * Add a user authentication profile + * * @param userAuth The user auth profile to add - * @return True if it was successful - * @throws IOException On any file writing error + * @throws IOException Thrown on any file writing error */ - boolean addUserAuth(UserAuth userAuth) throws IOException; + void addUserAuth(UserAuth userAuth) throws IOException; + + /** + * Remove a user authentication profile + * + * @param key The key of the user auth profile to remove + * @throws IOException Thrown on any file writing error + */ + void removeUserAuth(String key) throws IOException; } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java index 67918cc..4494939 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java @@ -24,6 +24,11 @@ public class UserAuthFIleDAO implements UserAuthDAO { load(); } + /** + * Loads the data from the file into the map + * + * @throws IOException Thrown if there was an issue reading the file + */ private void load() throws IOException { userAuthMap.clear(); @@ -34,29 +39,35 @@ public class UserAuthFIleDAO implements UserAuthDAO { } } + /** + * Saves the data from the map into the json file + * + * @throws IOException Thrown on any problem writing the file + */ private void save() throws IOException { objectMapper.writeValue(new File(filename), userAuthMap.values()); } - public UserAuth[] getAuthKeys() { + @Override + public UserAuth getUserAuth(String key) { synchronized (userAuthMap) { - return userAuthMap.values().toArray(UserAuth[]::new); + return userAuthMap.get(key); } } @Override - public UserAuth getUserAuth(String key) { + public void addUserAuth(UserAuth userAuth) throws IOException { synchronized (userAuthMap) { - return userAuthMap.get(key); + userAuthMap.put(userAuth.getKey(), userAuth); } + save(); } @Override - public boolean addUserAuth(UserAuth userAuth) throws IOException { + public void removeUserAuth(String key) throws IOException { synchronized (userAuthMap) { - userAuthMap.put(userAuth.getKey(), userAuth); - save(); - return true; + userAuthMap.remove(key); } + save(); } } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java index dca812b..1ef3032 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java @@ -2,6 +2,7 @@ package com.ufund.api.ufundapi.persistence; import java.io.File; import java.io.IOException; +import java.util.HashMap; import java.util.Map; import java.util.TreeMap; @@ -21,7 +22,7 @@ public class UserFileDAO implements UserDAO { public UserFileDAO(@Value("${users.file}") String filename, ObjectMapper objectMapper) throws IOException { this.filename = filename; this.objectMapper = objectMapper; - users = new TreeMap<>(); + users = new HashMap<>(); load(); // load the users from the file } @@ -47,25 +48,14 @@ public class UserFileDAO implements UserDAO { * @throws IOException If there was an IO issue saving the file */ private boolean save() throws IOException { - User[] userArray = getUserArray(); - - objectMapper.writeValue(new File(filename), userArray); + objectMapper.writeValue(new File(filename), users.values()); return true; } - /** - * Return an array of the needs - * - * @return An array of all the needs - */ - private User[] getUserArray() { - return users.values().toArray(User[]::new); - } - @Override - public User[] getUsers() throws IOException { + public User[] getUsers() { synchronized (users) { - return getUserArray(); + return users.values().toArray(User[]::new); } } @@ -75,10 +65,9 @@ public class UserFileDAO implements UserDAO { * @param username Name of desired user * * @return Desired user, null otherwise - * @throws IOException If there was an IO issue saving the file */ @Override - public User getUser(String username) throws IOException { + public User getUser(String username) { synchronized (users) { return users.getOrDefault(username, null); } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java index 2e644ee..ac86ff1 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java @@ -17,17 +17,29 @@ public class AuthService { this.userService = userService; } - public UserAuth getUserAuth(String key) { - return userAuthDAO.getUserAuth(key); - } - + /** + * Check if the provided key has access to the provided user. + * + * @param username The username of the user trying to be accessed. + * @param key The api key obtained by the client from logging in. + * @throws IllegalAccessException Thrown if access was denied to the user. + */ public void authenticate(String username, String key) throws IllegalAccessException { - var userAuth = getUserAuth(key); + var userAuth = userAuthDAO.getUserAuth(key); if (userAuth == null || !userAuth.getUsername().equals(username)) { throw new IllegalAccessException("Unauthorized"); } } + /** + * Attempt to log in with the provided credentials + * + * @param username The username of the user + * @param password The password of the user + * @return An API key if the authentication was successful. + * @throws IllegalAccessException Thrown if the username or password was incorrect + * @throws IOException If there was an issue saving the authentication + */ public String login(String username, String password) throws IllegalAccessException, IOException { var usr = userService.getUser(username); if (usr == null || !usr.verifyPassword(password)) { @@ -38,4 +50,14 @@ public class AuthService { return userAuth.getKey(); } + /** + * Logs out the current user + * + * @param key The API key to of the client + * @throws IOException Thrown if there was an error saving the authentication + */ + public void logout(String key) throws IOException { + userAuthDAO.removeUserAuth(key); + } + } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java index 860a2a8..6052e4f 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java @@ -5,17 +5,17 @@ import java.util.Arrays; import com.ufund.api.ufundapi.model.Need; import com.ufund.api.ufundapi.persistence.CupboardDAO; +import org.springframework.stereotype.Component; +@Component public class CupboardService { private final CupboardDAO cupboardDAO; public class DuplicateKeyException extends Exception { - public DuplicateKeyException(String message) { super(message); } - } public CupboardService(CupboardDAO cupboardDAO) { @@ -57,27 +57,34 @@ public class CupboardService { } /** - * @param id - * @return - * @throws IOException + * Gets a need with the specified ID + * + * @param id the ID of the need + * @return The resulting Need or null if the need was not found */ public Need getNeed(int id) throws IOException { return cupboardDAO.getNeed(id); } /** - * + * Modify a need + * * @param need * @return - * @throws IOException + * @throws IOException Thrown if there was an issue saving the changes */ public Need updateNeed(Need need) throws IOException { return cupboardDAO.updateNeed(need); } + /** + * Delete a need from the cupboard + * + * @param id the ID of the need + * @return True if the need was deleted + * @throws IOException Thrown on any problem removing the need + */ public boolean deleteNeed(int id) throws IOException { return cupboardDAO.deleteNeed(id); } - - } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java index a545029..6af3897 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java @@ -16,7 +16,7 @@ public class UserService { * * @param userDao The Data Access Object */ - public UserService(UserDAO userDao, AuthService authService) { + public UserService(UserDAO userDao) { this.userDAO = userDao; } diff --git a/ufund-api/src/main/resources/application.properties b/ufund-api/src/main/resources/application.properties index a866f98..70cb171 100644 --- a/ufund-api/src/main/resources/application.properties +++ b/ufund-api/src/main/resources/application.properties @@ -3,6 +3,7 @@ server.error.include-message=always cupboard.file=data/cupboard.json users.file=data/users.json +authKeys.file=data/userAuths.json spring.jackson.mapper.auto-detect-getters=false spring.jackson.mapper.auto-detect-setters=false diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java index 04ce41d..1cc84bf 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java @@ -1,7 +1,7 @@ package com.ufund.api.ufundapi.controller; import com.ufund.api.ufundapi.model.Need; -import com.ufund.api.ufundapi.persistence.CupboardFileDao; +import com.ufund.api.ufundapi.persistence.CupboardFileDAO; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; @@ -14,11 +14,11 @@ import static org.mockito.Mockito.when; public class CupboardControllerTest { private CupboardController cupboardController; - private CupboardFileDao mockCupboardDAO; + private CupboardFileDAO mockCupboardDAO; @BeforeEach public void setupCupboardDAO() { - mockCupboardDAO = mock(CupboardFileDao.class); + mockCupboardDAO = mock(CupboardFileDAO.class); cupboardController = new CupboardController(mockCupboardDAO); } diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java new file mode 100644 index 0000000..e554f9d --- /dev/null +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java @@ -0,0 +1,107 @@ +package com.ufund.api.ufundapi.persistence; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ufund.api.ufundapi.model.Need; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import com.ufund.api.ufundapi.model.Need.GoalType; + +@Tag("Persistence-tier") +public class CupboardFileDAOTest { + CupboardFileDAO cupboardFileDao; + Need[] testNeeds; + ObjectMapper mockObjectMapper; + + @BeforeEach + public void setupCupboardFileDao() throws IOException { + mockObjectMapper = mock(ObjectMapper.class); + testNeeds = new Need[3]; + testNeeds[0] = new Need("one", 0, 100, Need.GoalType.MONETARY); + testNeeds[1] = new Need("two", 1, 100, Need.GoalType.MONETARY); + testNeeds[2] = new Need("three", 2, 100, Need.GoalType.MONETARY); + // When the object mapper is supposed to read from the file + // the mock object mapper will return the hero array above + when(mockObjectMapper + .readValue(new File("doesnt_matter.txt"),Need[].class)) + .thenReturn(testNeeds); + cupboardFileDao = new CupboardFileDAO("doesnt_matter.txt",mockObjectMapper); + } + + @Test + public void GetNeedsTest() throws IOException { + Need[] needs = cupboardFileDao.getNeeds(); + assertEquals(needs.length,testNeeds.length); + assertEquals(needs[0].getName(), testNeeds[0].getName()); + } + + @Test + public void GetNeedTest() throws IOException { + Need need1 = cupboardFileDao.getNeed(0); + + assertEquals(testNeeds[0], need1); + } + + @Test + public void Fet() throws IOException { + String targetName1 = "one"; + String targetName2 = "two"; + + Need need1 = cupboardFileDao.findNeeds(targetName1)[0]; + Need need2 = cupboardFileDao.findNeeds(targetName2)[0]; + + assertEquals(testNeeds[0], need1); + assertEquals(testNeeds[1], need2); + } + + @Test + public void CreateNeedTest() throws IOException { + Need newNeed = new Need("sea urchin hats", 3, 100, GoalType.PHYSICAL); + + + Need actualNeed = cupboardFileDao.createNeed(newNeed); + + assertNotNull(actualNeed); + + assertEquals(actualNeed.getName(), newNeed.getName()); + } + + @Test + public void DeleteNeedTest() throws IOException { + Need undeletedNeed = cupboardFileDao.getNeed(0); + assertNotNull(undeletedNeed); + + boolean isDeleted = cupboardFileDao.deleteNeed(0); + assertTrue(isDeleted); + + Need deletedNeed = cupboardFileDao.getNeed(0); + assertNull(deletedNeed); + } + + @Test + public void UpdateNeedTest() throws IOException { + Need[] needs = cupboardFileDao.getNeeds(); + Need unupdatedNeed = needs[needs.length - 1]; + assertNotNull(unupdatedNeed); + + Need updatedNeed = new Need("sequin sea urchin hats", 2, 100, GoalType.PHYSICAL); + + Need actualNeed = cupboardFileDao.updateNeed(updatedNeed); + assertEquals(actualNeed, updatedNeed); + assertNotEquals(actualNeed, unupdatedNeed); + } + +} diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDaoTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDaoTest.java deleted file mode 100644 index 8aa6fe0..0000000 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDaoTest.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.ufund.api.ufundapi.persistence; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.io.IOException; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.ufund.api.ufundapi.model.Need; -import com.ufund.api.ufundapi.model.User; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - -import com.ufund.api.ufundapi.model.Need.GoalType; - -@Tag("Persistence-tier") -public class CupboardFileDaoTest { - CupboardFileDao cupboardFileDao; - Need[] testNeeds; - ObjectMapper mockObjectMapper; - - @BeforeEach - public void setupCupboardFileDao() throws IOException { - mockObjectMapper = mock(ObjectMapper.class); - testNeeds = new Need[3]; - testNeeds[0] = new Need("one", 0, 100, Need.GoalType.MONETARY); - testNeeds[1] = new Need("two", 1, 100, Need.GoalType.MONETARY); - testNeeds[2] = new Need("three", 2, 100, Need.GoalType.MONETARY); - // When the object mapper is supposed to read from the file - // the mock object mapper will return the hero array above - when(mockObjectMapper - .readValue(new File("doesnt_matter.txt"),Need[].class)) - .thenReturn(testNeeds); - cupboardFileDao = new CupboardFileDao("doesnt_matter.txt",mockObjectMapper); - } - - @Test - public void GetNeedsTest() throws IOException { - Need[] needs = cupboardFileDao.getNeeds(); - assertEquals(needs.length,testNeeds.length); - assertEquals(needs[0].getName(), testNeeds[0].getName()); - } - - @Test - public void GetNeedTest() throws IOException { - Need need1 = cupboardFileDao.getNeed(0); - - assertEquals(testNeeds[0], need1); - } - - @Test - public void Fet() throws IOException { - String targetName1 = "one"; - String targetName2 = "two"; - - Need need1 = cupboardFileDao.findNeeds(targetName1)[0]; - Need need2 = cupboardFileDao.findNeeds(targetName2)[0]; - - assertEquals(testNeeds[0], need1); - assertEquals(testNeeds[1], need2); - } - - @Test - public void CreateNeedTest() throws IOException { - Need newNeed = new Need("sea urchin hats", 3, 100, GoalType.PHYSICAL); - - - Need actualNeed = cupboardFileDao.createNeed(newNeed); - - assertNotNull(actualNeed); - - assertEquals(actualNeed.getName(), newNeed.getName()); - } - - @Test - public void DeleteNeedTest() throws IOException { - Need undeletedNeed = cupboardFileDao.getNeed(0); - assertNotNull(undeletedNeed); - - boolean isDeleted = cupboardFileDao.deleteNeed(0); - assertTrue(isDeleted); - - Need deletedNeed = cupboardFileDao.getNeed(0); - assertNull(deletedNeed); - } - - @Test - public void UpdateNeedTest() throws IOException { - Need[] needs = cupboardFileDao.getNeeds(); - Need unupdatedNeed = needs[needs.length - 1]; - assertNotNull(unupdatedNeed); - - Need updatedNeed = new Need("sequin sea urchin hats", 2, 100, GoalType.PHYSICAL); - - Need actualNeed = cupboardFileDao.updateNeed(updatedNeed); - assertEquals(actualNeed, updatedNeed); - assertNotEquals(actualNeed, unupdatedNeed); - } - -} diff --git a/ufund-ui/src/app/app.module.ts b/ufund-ui/src/app/app.module.ts index d818841..9203e3b 100644 --- a/ufund-ui/src/app/app.module.ts +++ b/ufund-ui/src/app/app.module.ts @@ -9,6 +9,8 @@ import {FundingBasketComponent} from './components/funding-basket/funding-basket import {CupboardComponent} from './components/cupboard/cupboard.component'; import {NeedListComponent} from './components/need-list/need-list.component'; import {HttpClientModule} from '@angular/common/http'; +import {FormsModule} from '@angular/forms'; +import {RouterLink, RouterLinkActive, RouterOutlet} from '@angular/router'; @NgModule({ declarations: [ @@ -22,6 +24,10 @@ import {HttpClientModule} from '@angular/common/http'; imports: [ BrowserModule, AppRoutingModule, + FormsModule, + RouterLink, + RouterLinkActive, + RouterOutlet, HttpClientModule, ], providers: [], diff --git a/ufund-ui/src/app/components/dashboard/dashboard.component.html b/ufund-ui/src/app/components/dashboard/dashboard.component.html index 9c5fce9..f41ccef 100644 --- a/ufund-ui/src/app/components/dashboard/dashboard.component.html +++ b/ufund-ui/src/app/components/dashboard/dashboard.component.html @@ -1 +1,3 @@

dashboard works!

+Go to the Cupboard +Go to my basket diff --git a/ufund-ui/src/app/components/dashboard/dashboard.component.ts b/ufund-ui/src/app/components/dashboard/dashboard.component.ts index 6da4013..dd323c4 100644 --- a/ufund-ui/src/app/components/dashboard/dashboard.component.ts +++ b/ufund-ui/src/app/components/dashboard/dashboard.component.ts @@ -7,5 +7,5 @@ import { Component } from '@angular/core'; styleUrl: './dashboard.component.css' }) export class DashboardComponent { - + constructor() {} } diff --git a/ufund-ui/src/app/components/login/login.component.ts b/ufund-ui/src/app/components/login/login.component.ts index 9a4eb0f..50dd018 100644 --- a/ufund-ui/src/app/components/login/login.component.ts +++ b/ufund-ui/src/app/components/login/login.component.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core' import {UsersService} from '../../services/users.service'; +import {Router} from '@angular/router'; @Component({ selector: 'app-login', @@ -9,7 +10,8 @@ import {UsersService} from '../../services/users.service'; }) export class LoginComponent { constructor( - protected usersService: UsersService + protected usersService: UsersService, + private router: Router ) {} login(username: string | null, password: string | null) { @@ -18,6 +20,8 @@ export class LoginComponent { return; } - this.usersService.login(username, password) + this.usersService.login(username, password).then(() => { + this.router.navigate(['/dashboard']); + }) } } -- cgit v1.2.3 From 953db99263178bcf122a415b50765fa283a8f42e Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 6 Mar 2025 21:49:18 -0500 Subject: Removed unused import --- .../src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java | 1 - 1 file changed, 1 deletion(-) diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java index 1ef3032..9b206c8 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java @@ -4,7 +4,6 @@ import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; -import java.util.TreeMap; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -- cgit v1.2.3 From 7cb123c21bef247a2216545bc18245136f2ddf78 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 6 Mar 2025 21:49:56 -0500 Subject: Added IOException throw to authenticate --- .../java/com/ufund/api/ufundapi/service/AuthService.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java index ac86ff1..7e54cfb 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java @@ -1,10 +1,11 @@ package com.ufund.api.ufundapi.service; -import com.ufund.api.ufundapi.model.UserAuth; -import com.ufund.api.ufundapi.persistence.UserAuthDAO; +import java.io.IOException; + import org.springframework.stereotype.Component; -import java.io.IOException; +import com.ufund.api.ufundapi.model.UserAuth; +import com.ufund.api.ufundapi.persistence.UserAuthDAO; @Component public class AuthService { @@ -23,8 +24,9 @@ public class AuthService { * @param username The username of the user trying to be accessed. * @param key The api key obtained by the client from logging in. * @throws IllegalAccessException Thrown if access was denied to the user. - */ - public void authenticate(String username, String key) throws IllegalAccessException { + * @throws IOException + */ + public void authenticate(String username, String key) throws IllegalAccessException, IOException { var userAuth = userAuthDAO.getUserAuth(key); if (userAuth == null || !userAuth.getUsername().equals(username)) { throw new IllegalAccessException("Unauthorized"); -- cgit v1.2.3 From a3fbcd713ae9a6b3f38dcc42a5c4c2f369a5d6f5 Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 6 Mar 2025 22:53:36 -0500 Subject: more javadocs and cleanup --- .../ufund/api/ufundapi/DuplicateKeyException.java | 7 ++++ .../ufundapi/controller/CupboardController.java | 4 +- .../api/ufundapi/controller/UserController.java | 11 ++--- .../api/ufundapi/persistence/UserAuthFIleDAO.java | 4 +- .../api/ufundapi/persistence/UserFileDAO.java | 32 --------------- .../ufund/api/ufundapi/service/AuthService.java | 12 +++--- .../api/ufundapi/service/CupboardService.java | 27 +++++++++---- .../ufund/api/ufundapi/service/UserService.java | 47 +++++++++++++++++----- 8 files changed, 76 insertions(+), 68 deletions(-) create mode 100644 ufund-api/src/main/java/com/ufund/api/ufundapi/DuplicateKeyException.java diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/DuplicateKeyException.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/DuplicateKeyException.java new file mode 100644 index 0000000..69ce6c0 --- /dev/null +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/DuplicateKeyException.java @@ -0,0 +1,7 @@ +package com.ufund.api.ufundapi; + +public class DuplicateKeyException extends Exception { + public DuplicateKeyException(String message) { + super(message); + } +} diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java index dfcb8a3..15a741a 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java @@ -20,7 +20,7 @@ import org.springframework.web.bind.annotation.RestController; import com.ufund.api.ufundapi.model.Need; import com.ufund.api.ufundapi.model.Need.GoalType; import com.ufund.api.ufundapi.service.CupboardService; -import com.ufund.api.ufundapi.service.CupboardService.DuplicateKeyException; +import com.ufund.api.ufundapi.DuplicateKeyException; @RestController @RequestMapping("cupboard") @@ -50,7 +50,7 @@ public class CupboardController { public ResponseEntity createNeed(@RequestBody Map params) { String name = params.get("name"); int maxGoal = Integer.parseInt(params.get("maxGoal")); - Need.GoalType goalType = GoalType.valueOf(params.get("maxGoal")); + Need.GoalType goalType = GoalType.valueOf(params.get("goalType")); try { Need need = cupboardService.createNeed(name, maxGoal, goalType); diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java index 02526af..21cd1b3 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java @@ -5,6 +5,7 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import com.ufund.api.ufundapi.DuplicateKeyException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -20,12 +21,6 @@ public class UserController { private final UserService userService; private final AuthService authService; - /** - * Creates a UserController - * - * @param userService - * @param authService - */ public UserController(UserService userService, AuthService authService) { this.userService = userService; this.authService = authService; @@ -49,7 +44,8 @@ public class UserController { } else { return new ResponseEntity<>(HttpStatus.CONFLICT); } - + } catch (DuplicateKeyException ex) { + return new ResponseEntity<>(HttpStatus.CONFLICT); } catch (IOException ex) { return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } @@ -93,7 +89,6 @@ public class UserController { */ @PutMapping("/{username}") public ResponseEntity updateUser(@RequestBody User user, @PathVariable String username, @RequestHeader("jelly-api-key") String key) { - try { authService.authenticate(username, key); user = userService.updateUser(user, username); diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java index 4494939..1fc1e92 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java @@ -59,15 +59,15 @@ public class UserAuthFIleDAO implements UserAuthDAO { public void addUserAuth(UserAuth userAuth) throws IOException { synchronized (userAuthMap) { userAuthMap.put(userAuth.getKey(), userAuth); + save(); } - save(); } @Override public void removeUserAuth(String key) throws IOException { synchronized (userAuthMap) { userAuthMap.remove(key); + save(); } - save(); } } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java index 9b206c8..97bc378 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java @@ -58,13 +58,6 @@ public class UserFileDAO implements UserDAO { } } - /** - * Return the user with the String name name or null otherwise - * - * @param username Name of desired user - * - * @return Desired user, null otherwise - */ @Override public User getUser(String username) { synchronized (users) { @@ -72,14 +65,6 @@ public class UserFileDAO implements UserDAO { } } - /** - * Create a User user - * - * @param user User to create - * - * @return Desired created user - * @throws IOException If there was an IO issue saving the file - */ @Override public User addUser(User user) throws IOException { synchronized (users) { @@ -92,15 +77,6 @@ public class UserFileDAO implements UserDAO { } } - /** - * Update a user that matches the supplied name - * - * @param name The name of the user - * @param newUser New user data - * - * @return Desired user, null otherwise - * @throws IOException If there was an IO issue saving the file - */ @Override public User updateUser(User newUser, String name) throws IOException { synchronized (users) { @@ -114,14 +90,6 @@ public class UserFileDAO implements UserDAO { } } - /** - * Delete a user matching the name - * - * @param username The name of the user - * - * @return True if deleted, false otherwise - * @throws IOException If there was an IO issue saving the file - */ @Override public boolean deleteUser(String username) throws IOException { synchronized (users) { diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java index 7e54cfb..591d891 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java @@ -1,11 +1,10 @@ package com.ufund.api.ufundapi.service; -import java.io.IOException; - -import org.springframework.stereotype.Component; - import com.ufund.api.ufundapi.model.UserAuth; import com.ufund.api.ufundapi.persistence.UserAuthDAO; +import org.springframework.stereotype.Component; + +import java.io.IOException; @Component public class AuthService { @@ -24,9 +23,8 @@ public class AuthService { * @param username The username of the user trying to be accessed. * @param key The api key obtained by the client from logging in. * @throws IllegalAccessException Thrown if access was denied to the user. - * @throws IOException - */ - public void authenticate(String username, String key) throws IllegalAccessException, IOException { + */ + public void authenticate(String username, String key) throws IllegalAccessException, IOException { var userAuth = userAuthDAO.getUserAuth(key); if (userAuth == null || !userAuth.getUsername().equals(username)) { throw new IllegalAccessException("Unauthorized"); diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java index 6052e4f..15f8442 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java @@ -6,22 +6,27 @@ import java.util.Arrays; import com.ufund.api.ufundapi.model.Need; import com.ufund.api.ufundapi.persistence.CupboardDAO; import org.springframework.stereotype.Component; +import com.ufund.api.ufundapi.DuplicateKeyException; @Component public class CupboardService { private final CupboardDAO cupboardDAO; - public class DuplicateKeyException extends Exception { - public DuplicateKeyException(String message) { - super(message); - } - } - public CupboardService(CupboardDAO cupboardDAO) { this.cupboardDAO = cupboardDAO; } + /** + * Creates a new Need + * + * @param name The name of the need to create + * @param maxGoal The max goal of the new need + * @param goalType The goal type of the new need + * @return The need that was created + * @throws IOException Thrown if there was any issue saving the data + * @throws DuplicateKeyException If there already exists a need with the same name + */ public Need createNeed(String name, int maxGoal, Need.GoalType goalType) throws IOException, DuplicateKeyException { Need need = new Need(name, goalType, maxGoal); @@ -39,6 +44,12 @@ public class CupboardService { } + /** + * Get all the needs in the cupboard + * + * @return An array containing all needs + * @throws IOException Thrown if there was any issue saving the data + */ public Need[] getNeeds() throws IOException { return cupboardDAO.getNeeds(); } @@ -48,7 +59,7 @@ public class CupboardService { * * @param search The search substring * @return The requested array - * @throws IOException + * @throws IOException Thrown if there was any issue saving the data */ public Need[] searchNeeds(String search) throws IOException { return Arrays.stream(cupboardDAO.getNeeds()) @@ -68,7 +79,7 @@ public class CupboardService { /** * Modify a need - * + * // TODO * @param need * @return * @throws IOException Thrown if there was an issue saving the changes diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java index 6af3897..776d09a 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java @@ -2,6 +2,7 @@ package com.ufund.api.ufundapi.service; import java.io.IOException; +import com.ufund.api.ufundapi.DuplicateKeyException; import com.ufund.api.ufundapi.model.User; import com.ufund.api.ufundapi.persistence.UserDAO; import org.springframework.stereotype.Component; @@ -11,29 +12,57 @@ public class UserService { private final UserDAO userDAO; - /** - * Create a user controller to receive REST signals - * - * @param userDao The Data Access Object - */ public UserService(UserDAO userDao) { this.userDAO = userDao; } - public User createUser(String username, String password) throws IOException { + /** + * Creates a new user + * + * @param username The username of the user + * @param password The password of the user + * @return The created user object + * @throws IOException Thrown on any problem saving the file + */ + public User createUser(String username, String password) throws IOException, DuplicateKeyException { + if (userDAO.getUser(username) != null) { + throw new DuplicateKeyException("A user with this name already exists"); + } User user = User.create(username, password); return userDAO.addUser(user); } - public User getUser(String username) throws IOException, IllegalAccessException { + /** + * Gets a user with the given username + * + * @param username The username of the user + * @return The user object with that username + * @throws IOException If there was any problem saving the file + */ + public User getUser(String username) throws IOException { return userDAO.getUser(username); } - public User updateUser(User user, String name) throws IllegalAccessException, IOException { + /** + * Updates a user + * // TODO + * @param user + * @param name + * @return + * @throws IOException Thrown if there was any issue saving the data + */ + public User updateUser(User user, String name) throws IOException { return userDAO.updateUser(user, name); } - public Boolean deleteUser(String username) throws IllegalAccessException, IOException { + /** + * Deletes a user + * + * @param username The username of the user to delete + * @return True if the user was deleted + * @throws IOException Thrown if there was any issue saving the data + */ + public boolean deleteUser(String username) throws IOException { return userDAO.deleteUser(username); } -- cgit v1.2.3 From 34903015992ac0cd7719b662af3ceb54a801351c Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 7 Mar 2025 00:02:56 -0500 Subject: Finish update methods --- .../ufund/api/ufundapi/controller/CupboardController.java | 9 ++++++--- .../com/ufund/api/ufundapi/controller/UserController.java | 4 +++- .../java/com/ufund/api/ufundapi/persistence/UserDAO.java | 5 ++--- .../com/ufund/api/ufundapi/persistence/UserFileDAO.java | 8 ++++---- .../com/ufund/api/ufundapi/service/CupboardService.java | 14 +++++++++----- .../java/com/ufund/api/ufundapi/service/UserService.java | 15 +++++++++------ 6 files changed, 33 insertions(+), 22 deletions(-) diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java index 15a741a..7773028 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java @@ -1,6 +1,7 @@ package com.ufund.api.ufundapi.controller; import java.io.IOException; +import java.security.InvalidParameterException; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -141,15 +142,17 @@ public class CupboardController { * @param need The need to update * @return OK response and the need if it was successful, or INTERNAL_SERVER_ERROR if there was an issue */ - @PutMapping("") - public ResponseEntity updateNeed(@RequestBody Need need) { + @PutMapping("/{id}") + public ResponseEntity updateNeed(@RequestBody Need need, @PathVariable int id) { try { - Need updatedNeed = cupboardService.updateNeed(need); + Need updatedNeed = cupboardService.updateNeed(need, id); if (updatedNeed != null) { return new ResponseEntity<>(need, HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } + } catch (InvalidParameterException ex) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } catch (IOException e) { return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java index 21cd1b3..0bb3fcf 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java @@ -1,6 +1,7 @@ package com.ufund.api.ufundapi.controller; import java.io.IOException; +import java.security.InvalidParameterException; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -97,7 +98,8 @@ public class UserController { } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } - + } catch (InvalidParameterException ex) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } catch (IOException e) { return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } catch (IllegalAccessException e) { diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java index 6558ce2..29d46cf 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java @@ -49,15 +49,14 @@ public interface UserDAO { /** * Updates and saves a {@linkplain User user} * - * @param newUser {@link User user} object to be updated and saved - * @param name {@link String name} name of object to be updated + * @param user {@link User user} object to be updated and saved * * @return updated {@link User user} if successful, null if * {@link User user} could not be found * * @throws IOException if underlying storage cannot be accessed */ - User updateUser(User newUser, String name) throws IOException; + User updateUser(User user) throws IOException; /** * Deletes a {@linkplain User user} with the given id diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java index 97bc378..f17f8f2 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java @@ -78,12 +78,12 @@ public class UserFileDAO implements UserDAO { } @Override - public User updateUser(User newUser, String name) throws IOException { + public User updateUser(User user) throws IOException { synchronized (users) { - if (users.containsKey(name)) { - users.put(name, newUser); + if (users.containsKey(user.getUsername())) { + users.put(user.getUsername(), user); save(); - return newUser; + return user; } else { return null; } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java index 15f8442..c8609ab 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java @@ -78,13 +78,17 @@ public class CupboardService { } /** - * Modify a need - * // TODO - * @param need - * @return + * Updates a need + * + * @param id The ID of the need to update + * @param need The need object to set (note: the ID is ignored) + * @return The updated need object * @throws IOException Thrown if there was an issue saving the changes */ - public Need updateNeed(Need need) throws IOException { + public Need updateNeed(Need need, int id) throws IOException { + if (need.getId() != id) { + throw new IllegalArgumentException("ID in URL and body must match"); + } return cupboardDAO.updateNeed(need); } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java index 776d09a..935ee72 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java @@ -45,14 +45,17 @@ public class UserService { /** * Updates a user - * // TODO - * @param user - * @param name - * @return + * + * @param user The ID of the user to update + * @param username The user object to set (note: the ID is ignored) + * @return The updated user object * @throws IOException Thrown if there was any issue saving the data */ - public User updateUser(User user, String name) throws IOException { - return userDAO.updateUser(user, name); + public User updateUser(User user, String username) throws IOException { + if (!user.getUsername().equals(username)) { + throw new IllegalArgumentException("ID in URL and body must match"); + } + return userDAO.updateUser(user); } /** -- cgit v1.2.3 From caaf278d1fa69fef69c210edb337fa54102d2737 Mon Sep 17 00:00:00 2001 From: sowgro Date: Fri, 7 Mar 2025 12:52:24 -0500 Subject: Fix login in angular --- ufund-ui/src/app/app-routing.module.ts | 2 +- ufund-ui/src/app/app.module.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ufund-ui/src/app/app-routing.module.ts b/ufund-ui/src/app/app-routing.module.ts index d4f14da..4b76654 100644 --- a/ufund-ui/src/app/app-routing.module.ts +++ b/ufund-ui/src/app/app-routing.module.ts @@ -12,7 +12,7 @@ const routes: Routes = [ {path: 'login', component: LoginComponent}, {path: 'cupboard', component: CupboardComponent}, {path: 'dashboard', component: DashboardComponent}, - {path: 'funding-basket', component: FundingBasketComponent}, + {path: 'basket', component: FundingBasketComponent}, {path: 'need/:id', component: NeedPageComponent} ]; diff --git a/ufund-ui/src/app/app.module.ts b/ufund-ui/src/app/app.module.ts index 9203e3b..fa54c58 100644 --- a/ufund-ui/src/app/app.module.ts +++ b/ufund-ui/src/app/app.module.ts @@ -11,6 +11,7 @@ import {NeedListComponent} from './components/need-list/need-list.component'; import {HttpClientModule} from '@angular/common/http'; import {FormsModule} from '@angular/forms'; import {RouterLink, RouterLinkActive, RouterOutlet} from '@angular/router'; +import {DashboardComponent} from './components/dashboard/dashboard.component'; @NgModule({ declarations: [ @@ -19,7 +20,8 @@ import {RouterLink, RouterLinkActive, RouterOutlet} from '@angular/router'; HomePageComponent, FundingBasketComponent, CupboardComponent, - NeedListComponent + NeedListComponent, + DashboardComponent ], imports: [ BrowserModule, -- cgit v1.2.3 From fa1f1140b1e13d495c8e06e80928efb333917d31 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Fri, 7 Mar 2025 15:35:31 -0500 Subject: Updated cupboard controller tests to use service class --- .../controller/CupboardControllerTest.java | 53 ++++++++++++++-------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java index 1cc84bf..a78c45c 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java @@ -1,33 +1,48 @@ package com.ufund.api.ufundapi.controller; import com.ufund.api.ufundapi.model.Need; -import com.ufund.api.ufundapi.persistence.CupboardFileDAO; +import com.ufund.api.ufundapi.model.Need.GoalType; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import static java.util.Map.entry; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.ufund.api.ufundapi.service.CupboardService; + public class CupboardControllerTest { private CupboardController cupboardController; - private CupboardFileDAO mockCupboardDAO; + private CupboardService mockCupboardService; @BeforeEach public void setupCupboardDAO() { - mockCupboardDAO = mock(CupboardFileDAO.class); - cupboardController = new CupboardController(mockCupboardDAO); + mockCupboardService = mock(CupboardService.class); + cupboardController = new CupboardController(mockCupboardService); } @Test - public void createNeed() throws IOException { - var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); - when(mockCupboardDAO.createNeed(need)).thenReturn(need); + public void createNeed() throws IOException, CupboardService.DuplicateKeyException { + String name = "Test"; + int maxGoal = 100; + GoalType type = Need.GoalType.MONETARY; + var need = new Need(name, type, maxGoal); + when(mockCupboardService.createNeed(name, maxGoal, type)).thenReturn(need); + + + Map needMap = Map.ofEntries( + entry("id", need.getId()), + entry("need", need) + ); - var res = cupboardController.createNeed(need); + var res = cupboardController.createNeed(needMap); assertEquals(HttpStatus.OK, res.getStatusCode()); assertEquals(need, res.getBody()); @@ -36,7 +51,7 @@ public class CupboardControllerTest { @Test public void getNeeds() { var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); - when(mockCupboardDAO.getNeeds()).thenReturn(new Need[]{need}); + when(mockCupboardService.getNeeds()).thenReturn(new Need[]{need}); var res = cupboardController.getNeeds(); @@ -46,7 +61,7 @@ public class CupboardControllerTest { @Test public void getNeedsEmpty() { - when(mockCupboardDAO.getNeeds()).thenReturn(new Need[]{}); + when(mockCupboardService.getNeeds()).thenReturn(new Need[]{}); var res = cupboardController.getNeeds(); @@ -57,7 +72,7 @@ public class CupboardControllerTest { @Test public void searchNeeds() { var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); - when(mockCupboardDAO.findNeeds("Na")).thenReturn(new Need[]{need}); + when(mockCupboardService.findNeeds("Na")).thenReturn(new Need[]{need}); var res = cupboardController.searchNeeds("Na"); @@ -67,7 +82,7 @@ public class CupboardControllerTest { @Test public void searchNeedsEmpty() { - when(mockCupboardDAO.findNeeds("Na")).thenReturn(new Need[]{}); + when(mockCupboardService.findNeeds("Na")).thenReturn(new Need[]{}); var res = cupboardController.searchNeeds("Na"); @@ -78,7 +93,7 @@ public class CupboardControllerTest { @Test public void getNeed() { var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); - when(mockCupboardDAO.getNeed(need.getId())).thenReturn(need); + when(mockCupboardService.getNeed(need.getId())).thenReturn(need); var res = cupboardController.getNeed(need.getId()); @@ -89,7 +104,7 @@ public class CupboardControllerTest { @Test public void getNeedFail() { var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); - when(mockCupboardDAO.getNeed(need.getId())).thenReturn(null); + when(mockCupboardService.getNeed(need.getId())).thenReturn(null); var res = cupboardController.getNeed(need.getId()); @@ -100,7 +115,7 @@ public class CupboardControllerTest { @Test public void updateNeeds() throws IOException { var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); - when(mockCupboardDAO.updateNeed(need)).thenReturn(need); + when(mockCupboardService.updateNeed(need)).thenReturn(need); var res = cupboardController.updateNeed(need); @@ -111,8 +126,8 @@ public class CupboardControllerTest { @Test public void deleteNeed() throws IOException { var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); - when(mockCupboardDAO.getNeed(1)).thenReturn(need); - when(mockCupboardDAO.deleteNeed(1)).thenReturn(true); + when(mockCupboardService.getNeed(1)).thenReturn(need); + when(mockCupboardService.deleteNeed(1)).thenReturn(true); var res = cupboardController.deleteNeed(1); @@ -121,8 +136,8 @@ public class CupboardControllerTest { @Test public void deleteNeedFail() throws IOException { - when(mockCupboardDAO.getNeed(1)).thenReturn(null); - when(mockCupboardDAO.deleteNeed(1)).thenReturn(false); + when(mockCupboardService.getNeed(1)).thenReturn(null); + when(mockCupboardService.deleteNeed(1)).thenReturn(false); var res = cupboardController.deleteNeed(1); -- cgit v1.2.3 From a9dd0b0035db327f8be91344115afc96c86b6210 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 13 Mar 2025 16:54:14 -0400 Subject: Fixed broken tests --- .../controller/CupboardControllerTest.java | 95 ++++++++++++---------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java index c7a5584..100bf09 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java @@ -1,21 +1,21 @@ package com.ufund.api.ufundapi.controller; -import com.ufund.api.ufundapi.model.Need; -import com.ufund.api.ufundapi.model.Need.GoalType; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; - import java.io.IOException; -import java.util.HashMap; import java.util.Map; import static java.util.Map.entry; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import org.springframework.http.HttpStatus; +import com.ufund.api.ufundapi.DuplicateKeyException; +import com.ufund.api.ufundapi.model.Need; +import com.ufund.api.ufundapi.model.Need.GoalType; import com.ufund.api.ufundapi.service.CupboardService; public class CupboardControllerTest { @@ -29,7 +29,7 @@ public class CupboardControllerTest { } @Test - public void createNeed() throws IOException, CupboardService.DuplicateKeyException { + public void createNeed() throws IOException, DuplicateKeyException { String name = "Test"; int maxGoal = 100; GoalType type = Need.GoalType.MONETARY; @@ -37,9 +37,10 @@ public class CupboardControllerTest { when(mockCupboardService.createNeed(name, maxGoal, type)).thenReturn(need); - Map needMap = Map.ofEntries( - entry("id", need.getId()), - entry("need", need) + Map needMap = Map.ofEntries( + entry("name", "Test"), + entry("maxGoal", "100"), + entry("goalType", "MONETARY") ); var res = cupboardController.createNeed(needMap); @@ -49,27 +50,35 @@ public class CupboardControllerTest { } @Test - public void createNeedBadMaxGoal() throws IOException { - var need = new Need("Name", 1, -100, Need.GoalType.MONETARY); - when(mockCupboardDAO.createNeed(need)).thenReturn(need); + public void createNeedBadMaxGoal() throws IOException, DuplicateKeyException { + when(mockCupboardService.createNeed("Name", -100, Need.GoalType.MONETARY)).thenThrow(new IllegalArgumentException()); - var res = cupboardController.createNeed(need); + Map needMap = Map.ofEntries( + entry("name", "Name"), + entry("maxGoal", "-100"), + entry("goalType", "MONETARY")); - assertEquals(HttpStatus.BAD_REQUEST, res.getStatusCode()); + var res = cupboardController.createNeed(needMap); + + assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, res.getStatusCode()); } @Test - public void createNeedIOException() throws IOException { - var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); - when(mockCupboardDAO.createNeed(need)).thenThrow(new IOException()); + public void createNeedIOException() throws IOException, DuplicateKeyException { + when(mockCupboardService.createNeed("Name", 100, Need.GoalType.MONETARY)).thenThrow(new IOException()); - var res = cupboardController.createNeed(need); + Map needMap = Map.ofEntries( + entry("name", "Name"), + entry("maxGoal", "100"), + entry("goalType", "MONETARY")); + + var res = cupboardController.createNeed(needMap); assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, res.getStatusCode()); } @Test - public void getNeeds() { + public void getNeeds() throws IOException { var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); when(mockCupboardService.getNeeds()).thenReturn(new Need[]{need}); @@ -80,9 +89,8 @@ public class CupboardControllerTest { } @Test - public void getNeedsIOException() { - var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); - when(mockCupboardDAO.getNeeds()).thenThrow(new IOException()); + public void getNeedsIOException() throws IOException { + when(mockCupboardService.getNeeds()).thenThrow(new IOException()); var res = cupboardController.getNeeds(); @@ -90,7 +98,7 @@ public class CupboardControllerTest { } @Test - public void getNeedsEmpty() { + public void getNeedsEmpty() throws IOException { when(mockCupboardService.getNeeds()).thenReturn(new Need[]{}); var res = cupboardController.getNeeds(); @@ -100,9 +108,9 @@ public class CupboardControllerTest { } @Test - public void searchNeeds() { + public void searchNeeds() throws IOException { var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); - when(mockCupboardService.findNeeds("Na")).thenReturn(new Need[]{need}); + when(mockCupboardService.searchNeeds("Na")).thenReturn(new Need[]{need}); var res = cupboardController.searchNeeds("Na"); @@ -111,9 +119,8 @@ public class CupboardControllerTest { } @Test - public void searchNeedsIOException() { - var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); - when(mockCupboardDAO.findNeeds("Na")).thenThrow(new IOException()); + public void searchNeedsIOException() throws IOException { + when(mockCupboardService.searchNeeds("Na")).thenThrow(new IOException()); var res = cupboardController.searchNeeds("Na"); @@ -121,8 +128,8 @@ public class CupboardControllerTest { } @Test - public void searchNeedsEmpty() { - when(mockCupboardService.findNeeds("Na")).thenReturn(new Need[]{}); + public void searchNeedsEmpty() throws IOException { + when(mockCupboardService.searchNeeds("Na")).thenReturn(new Need[]{}); var res = cupboardController.searchNeeds("Na"); @@ -131,7 +138,7 @@ public class CupboardControllerTest { } @Test - public void getNeed() { + public void getNeed() throws IOException { var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); when(mockCupboardService.getNeed(need.getId())).thenReturn(need); @@ -142,9 +149,9 @@ public class CupboardControllerTest { } @Test - public void getNeedIOException() { + public void getNeedIOException() throws IOException { var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); - when(mockCupboardDAO.getNeed(need.getId())).thenThrow(new IOException()); + when(mockCupboardService.getNeed(need.getId())).thenThrow(new IOException()); var res = cupboardController.getNeed(need.getId()); @@ -152,7 +159,7 @@ public class CupboardControllerTest { } @Test - public void getNeedFail() { + public void getNeedFail() throws IOException { var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); when(mockCupboardService.getNeed(need.getId())).thenReturn(null); @@ -165,9 +172,9 @@ public class CupboardControllerTest { @Test public void updateNeeds() throws IOException { var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); - when(mockCupboardService.updateNeed(need)).thenReturn(need); + when(mockCupboardService.updateNeed(need, 1)).thenReturn(need); - var res = cupboardController.updateNeed(need); + var res = cupboardController.updateNeed(need, 1); assertEquals(HttpStatus.OK, res.getStatusCode()); assertEquals(need, res.getBody()); @@ -176,9 +183,9 @@ public class CupboardControllerTest { @Test public void updateNeedsIOException() throws IOException { var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); - when(mockCupboardDAO.updateNeed(need)).thenThrow(new IOException()); + when(mockCupboardService.updateNeed(need, 1)).thenThrow(new IOException()); - var res = cupboardController.updateNeed(need); + var res = cupboardController.updateNeed(need, 1); assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, res.getStatusCode()); } @@ -207,8 +214,8 @@ public class CupboardControllerTest { @Test public void deleteNeedIOException() throws IOException { var need = new Need("Name", 1, 100, Need.GoalType.MONETARY); - when(mockCupboardDAO.getNeed(1)).thenReturn(need); - when(mockCupboardDAO.deleteNeed(1)).thenThrow(new IOException()); + when(mockCupboardService.getNeed(1)).thenReturn(need); + when(mockCupboardService.deleteNeed(1)).thenThrow(new IOException()); var res = cupboardController.deleteNeed(1); -- cgit v1.2.3 From 183d4b047f69c1f6daed8e6ee8eb257a52d97e32 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 13 Mar 2025 16:54:21 -0400 Subject: Updated imports --- .../com/ufund/api/ufundapi/controller/AuthController.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java index b0390ae..b46d4ee 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java @@ -1,13 +1,18 @@ package com.ufund.api.ufundapi.controller; -import com.ufund.api.ufundapi.service.AuthService; -import com.ufund.api.ufundapi.service.UserService; +import java.io.IOException; +import java.util.Map; + import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; -import java.io.IOException; -import java.util.Map; +import com.ufund.api.ufundapi.service.AuthService; @RestController @RequestMapping("auth") -- cgit v1.2.3 From beeb08675f45b5ad45d461642919d13a5a9c458e Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 13 Mar 2025 17:37:58 -0400 Subject: Fixed broken tests --- .../api/ufundapi/persistence/UserFileDAOTest.java | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java index 52a1fdc..ba39130 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java @@ -1,24 +1,22 @@ package com.ufund.api.ufundapi.persistence; +import java.io.File; +import java.io.IOException; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.io.File; -import java.io.IOException; - import com.fasterxml.jackson.databind.ObjectMapper; - import com.ufund.api.ufundapi.model.User; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - @Tag("Persistence-tier") public class UserFileDAOTest { UserFileDAO userFileDAO; @@ -102,11 +100,11 @@ public class UserFileDAOTest { User toBeUpdatedUser = userFileDAO.getUser("admin"); assertNotNull(toBeUpdatedUser); - User updatedUser = new User("jellinadmin"); + User updatedUser = User.create("admin", "newPass"); - updatedUser = userFileDAO.updateUser(updatedUser, "admin"); + updatedUser = userFileDAO.updateUser(updatedUser); assertNotEquals(toBeUpdatedUser, updatedUser); - assertEquals("jellinadmin", updatedUser.getUsername()); + assertEquals("admin", updatedUser.getUsername()); } } -- cgit v1.2.3 From bae0f05fb971b7ec99f4279743e602a418553e45 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 13 Mar 2025 17:44:26 -0400 Subject: Updated docstrings --- .../api/ufundapi/controller/UserController.java | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java index 0bb3fcf..795ca13 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java @@ -6,11 +6,19 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; -import com.ufund.api.ufundapi.DuplicateKeyException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ufund.api.ufundapi.DuplicateKeyException; import com.ufund.api.ufundapi.model.User; import com.ufund.api.ufundapi.service.AuthService; import com.ufund.api.ufundapi.service.UserService; @@ -29,7 +37,7 @@ public class UserController { /** * Creates a User with the provided object - * + * @param params A map consisting of the parameters for a user * @return OK response and the user if it was successful, INTERNAL_SERVER_ERROR * otherwise */ @@ -55,6 +63,8 @@ public class UserController { /** * Responds to the GET request for a {@linkplain User user} for the given id * + * @param username The name of the user + * @param key The authentication key of the user * @return ResponseEntity with {@link User user} object and HTTP status of OK if * found
* ResponseEntity with HTTP status of NOT_FOUND if not found
@@ -84,7 +94,9 @@ public class UserController { /** * Updates a User with the provided one * - * @param user The user to update + * @param user The user to update + * @param username The name of the user + * @param key The authentication key of the user * @return OK response and the user if it was successful, or * INTERNAL_SERVER_ERROR if there was an issue */ @@ -111,6 +123,7 @@ public class UserController { * Deletes a user with the desired name * * @param username The name of the user + * @param key The authentication key of the user * @return OK if the user was deleted, NOT_FOUND if the user was not found, or * INTERNAL_SERVER_ERROR if an error occurred */ -- cgit v1.2.3 From 4caaeec30f8732658dbe9ad053253d5cb483efca Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 13 Mar 2025 20:38:41 -0400 Subject: Updated tests --- .../src/main/java/com/ufund/api/ufundapi/controller/UserController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java index 795ca13..adf17a1 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java @@ -48,7 +48,7 @@ public class UserController { try { User user = userService.createUser(username, password); - if (user == null) { + if (user != null) { return new ResponseEntity<>(user, HttpStatus.CREATED); } else { return new ResponseEntity<>(HttpStatus.CONFLICT); -- cgit v1.2.3 From 7ed118ef9af842d7a376f53d1463529db3c796d8 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Thu, 13 Mar 2025 20:38:48 -0400 Subject: Fixed broken tests --- .../ufundapi/controller/UserControllerTest.java | 93 +++++++++++++--------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java index d189836..11fa6a4 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java @@ -1,7 +1,8 @@ package com.ufund.api.ufundapi.controller; import java.io.IOException; -import java.util.HashMap; +import java.util.Map; +import static java.util.Map.entry; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.BeforeEach; @@ -13,22 +14,23 @@ import static org.mockito.Mockito.when; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import com.ufund.api.ufundapi.DuplicateKeyException; import com.ufund.api.ufundapi.model.User; import com.ufund.api.ufundapi.model.UserAuth; -import com.ufund.api.ufundapi.persistence.UserAuthFIleDAO; -import com.ufund.api.ufundapi.persistence.UserFileDAO; +import com.ufund.api.ufundapi.service.AuthService; +import com.ufund.api.ufundapi.service.UserService; @Tag("Controller-tier") public class UserControllerTest { private UserController userController; - private UserFileDAO mockUserDAO; - private UserAuthFIleDAO mockAuthUserDAO; + private UserService mockUserService; + private AuthService mockAuthService; @BeforeEach public void setupUserController() { - mockUserDAO = mock(UserFileDAO.class); - mockAuthUserDAO = mock(UserAuthFIleDAO.class); - userController = new UserController(mockUserDAO, mockAuthUserDAO); + mockUserService = mock(UserService.class); + mockAuthService = mock(AuthService.class); + userController = new UserController(mockUserService, mockAuthService); } @Test @@ -38,7 +40,7 @@ public class UserControllerTest { User user = new User(username); String key = UserAuth.generate(username).getKey(); // When the same id is passed in, our mock User DAO will return the User object - when(mockUserDAO.getUser(username)).thenReturn(user); + when(mockUserService.getUser(username)).thenReturn(user); // Invoke @@ -56,7 +58,7 @@ public class UserControllerTest { String key = UserAuth.generate(username).getKey(); // When the same id is passed in, our mock User DAO will return null, simulating // no User found - when(mockUserDAO.getUser(username)).thenReturn(null); + when(mockUserService.getUser(username)).thenReturn(null); // Invoke @@ -72,7 +74,7 @@ public class UserControllerTest { String username = "Test"; String key = UserAuth.generate(username).getKey(); // When getUser is called on the Mock User DAO, throw an IOException - doThrow(new IOException()).when(mockUserDAO).getUser(username); + doThrow(new IOException()).when(mockUserService).getUser(username); // Invoke ResponseEntity response = userController.getUser(username, key); @@ -87,19 +89,22 @@ public class UserControllerTest { ****************************************************************/ @Test - public void testCreateUser() throws IOException { // createUser may throw IOException + public void testCreateUser() throws IOException, DuplicateKeyException { // createUser may throw IOException // Setup String username = "Test"; + String password = "Pass"; User user = new User(username); - String key = UserAuth.generate(username).getKey(); // when createUser is called, return true simulating successful // creation and save - when(mockUserDAO.addUser(user)).thenReturn(user); + when(mockUserService.createUser(username, password)).thenReturn(user); - + Map userMap = Map.ofEntries( + entry("username", "Test"), + entry("password", "Pass") + ); // Invoke - ResponseEntity response = userController.createUser(params); + ResponseEntity response = userController.createUser(userMap); // Analyze assertEquals(HttpStatus.CREATED, response.getStatusCode()); @@ -107,32 +112,42 @@ public class UserControllerTest { } @Test - public void testCreateUserFailed() throws IOException { // createUser may throw IOException + public void testCreateUserFailed() throws IOException, DuplicateKeyException { // createUser may throw IOException // Setup String username = "Test"; - User user = new User(username); + String password = "Pass"; // when createUser is called, return false simulating failed // creation and save - when(mockUserDAO.addUser(user)).thenReturn(null); + when(mockUserService.createUser(username, password)).thenReturn(null); + + Map userMap = Map.ofEntries( + entry("username", "Test"), + entry("password", "Pass") + ); // Invoke - ResponseEntity response = userController.createUser(user); + ResponseEntity response = userController.createUser(userMap); // Analyze assertEquals(HttpStatus.CONFLICT, response.getStatusCode()); } @Test - public void testCreateUserHandleException() throws IOException { // createUser may throw IOException + public void testCreateUserHandleException() throws IOException, DuplicateKeyException { // createUser may throw IOException // Setup String username = "Test"; - User user = new User(username); + String password = "Pass"; // When createUser is called on the Mock User DAO, throw an IOException - doThrow(new IOException()).when(mockUserDAO).addUser(user); + doThrow(new IOException()).when(mockUserService).createUser(username, password); + + Map userMap = Map.ofEntries( + entry("username", "Test"), + entry("password", "Pass") + ); // Invoke - ResponseEntity response = userController.createUser(user); + ResponseEntity response = userController.createUser(userMap); // Analyze assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); @@ -143,12 +158,13 @@ public class UserControllerTest { // Setup String username = "Test"; User user = new User("Bob"); + String key = UserAuth.generate(username).getKey(); // when updateUser is called, return true simulating successful // update and save - when(mockUserDAO.updateUser(user, username)).thenReturn(user); + when(mockUserService.updateUser(user, username)).thenReturn(user); // Invoke - ResponseEntity response = userController.updateUser(user, username); + ResponseEntity response = userController.updateUser(user, username, key); // Analyze assertEquals(HttpStatus.OK, response.getStatusCode()); @@ -160,12 +176,13 @@ public class UserControllerTest { // Setup String username = "Test"; User user = new User("Bob"); + String key = UserAuth.generate(username).getKey(); // when updateUser is called, return true simulating successful // update and save - when(mockUserDAO.updateUser(user, username)).thenReturn(null); + when(mockUserService.updateUser(user, username)).thenReturn(null); // Invoke - ResponseEntity response = userController.updateUser(user, username); + ResponseEntity response = userController.updateUser(user, username, key); // Analyze assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); @@ -176,11 +193,12 @@ public class UserControllerTest { // Setup String username = "Test"; User user = new User("Bob"); + String key = UserAuth.generate(username).getKey(); // When updateUser is called on the Mock User DAO, throw an IOException - doThrow(new IOException()).when(mockUserDAO).updateUser(user, username); + doThrow(new IOException()).when(mockUserService).updateUser(user, username); // Invoke - ResponseEntity response = userController.updateUser(user, username); + ResponseEntity response = userController.updateUser(user, username, key); // Analyze assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); @@ -190,11 +208,12 @@ public class UserControllerTest { public void testDeleteUser() throws IOException { // deleteUser may throw IOException // Setup String username = "Test"; + String key = UserAuth.generate(username).getKey(); // when deleteUser is called return true, simulating successful deletion - when(mockUserDAO.deleteUser(username)).thenReturn(true); + when(mockUserService.deleteUser(username)).thenReturn(true); // Invoke - ResponseEntity response = userController.deleteUser(username); + ResponseEntity response = userController.deleteUser(username, key); // Analyze assertEquals(HttpStatus.OK, response.getStatusCode()); @@ -204,11 +223,12 @@ public class UserControllerTest { public void testDeleteUserNotFound() throws IOException { // deleteUser may throw IOException // Setup String username = "Test"; + String key = UserAuth.generate(username).getKey(); // when deleteUser is called return false, simulating failed deletion - when(mockUserDAO.deleteUser(username)).thenReturn(false); + when(mockUserService.deleteUser(username)).thenReturn(false); // Invoke - ResponseEntity response = userController.deleteUser(username); + ResponseEntity response = userController.deleteUser(username, key); // Analyze assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); @@ -218,11 +238,12 @@ public class UserControllerTest { public void testDeleteUserHandleException() throws IOException { // deleteUser may throw IOException // Setup String username = "Test"; + String key = UserAuth.generate(username).getKey(); // When deleteUser is called on the Mock User DAO, throw an IOException - doThrow(new IOException()).when(mockUserDAO).deleteUser(username); + doThrow(new IOException()).when(mockUserService).deleteUser(username); // Invoke - ResponseEntity response = userController.deleteUser(username); + ResponseEntity response = userController.deleteUser(username, key); // Analyze assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); -- cgit v1.2.3 From 30c301ed050cb0deeb9755b28300c311610b7f98 Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 13 Mar 2025 20:47:44 -0400 Subject: Fix and clean up remaining tests --- .../com/ufund/api/ufundapi/model/NeedTest.java | 6 +++--- .../com/ufund/api/ufundapi/model/UserTest.java | 6 +++--- .../ufundapi/persistence/CupboardFileDAOTest.java | 24 ++++++---------------- .../api/ufundapi/persistence/UserFileDAOTest.java | 19 +++++++++-------- 4 files changed, 22 insertions(+), 33 deletions(-) diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/model/NeedTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/model/NeedTest.java index ffcd808..772af5c 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/model/NeedTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/model/NeedTest.java @@ -48,15 +48,15 @@ public class NeedTest { double current = 0.00; need.setCurrent(current); - assertEquals(need.getCurrent(), current); + assertEquals(current, need.getCurrent()); current = 100.00; need.setCurrent(current); - assertEquals(need.getCurrent(), current); + assertEquals(current, need.getCurrent()); current = -100.00; need.setCurrent(current); - assertEquals(need.getCurrent(), current); + assertEquals(current, need.getCurrent()); } diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java index 22f6ffb..1725190 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java @@ -1,10 +1,10 @@ package com.ufund.api.ufundapi.model; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + @Tag("Model-tier") public class UserTest { @@ -70,7 +70,7 @@ public class UserTest { User user = new User(expectedName); - assertEquals(false, user.verifyPassword(expectedName)); + assertFalse(user.verifyPassword(expectedName)); } diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java index e554f9d..cbfa30c 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java @@ -42,37 +42,25 @@ public class CupboardFileDAOTest { } @Test - public void GetNeedsTest() throws IOException { + public void getNeedsTest() { Need[] needs = cupboardFileDao.getNeeds(); assertEquals(needs.length,testNeeds.length); assertEquals(needs[0].getName(), testNeeds[0].getName()); } @Test - public void GetNeedTest() throws IOException { + public void getNeedTest() { Need need1 = cupboardFileDao.getNeed(0); assertEquals(testNeeds[0], need1); } @Test - public void Fet() throws IOException { - String targetName1 = "one"; - String targetName2 = "two"; - - Need need1 = cupboardFileDao.findNeeds(targetName1)[0]; - Need need2 = cupboardFileDao.findNeeds(targetName2)[0]; - - assertEquals(testNeeds[0], need1); - assertEquals(testNeeds[1], need2); - } - - @Test - public void CreateNeedTest() throws IOException { + public void createNeedTest() throws IOException { Need newNeed = new Need("sea urchin hats", 3, 100, GoalType.PHYSICAL); - Need actualNeed = cupboardFileDao.createNeed(newNeed); + Need actualNeed = cupboardFileDao.addNeed(newNeed); assertNotNull(actualNeed); @@ -80,7 +68,7 @@ public class CupboardFileDAOTest { } @Test - public void DeleteNeedTest() throws IOException { + public void deleteNeedTest() throws IOException { Need undeletedNeed = cupboardFileDao.getNeed(0); assertNotNull(undeletedNeed); @@ -92,7 +80,7 @@ public class CupboardFileDAOTest { } @Test - public void UpdateNeedTest() throws IOException { + public void updateNeedTest() throws IOException { Need[] needs = cupboardFileDao.getNeeds(); Need unupdatedNeed = needs[needs.length - 1]; assertNotNull(unupdatedNeed); diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java index ba39130..b802669 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java @@ -40,7 +40,7 @@ public class UserFileDAOTest { } @Test - public void GetUsersTest() throws IOException { + public void getUsersTest() { User[] users = userFileDAO.getUsers(); assertEquals(users.length,testUsers.length); @@ -48,16 +48,17 @@ public class UserFileDAOTest { for (int i = 0; i < testUsers.length;++i) { boolean isInArray = false; for (User user : testUsers) { - if (users[i].getUsername().equals(user.getUsername())) { - isInArray = true; - } + if (users[i].getUsername().equals(user.getUsername())) { + isInArray = true; + break; + } } assertTrue(isInArray); } } @Test - public void FindUsersTest() throws IOException { + public void findUsersTest() { User realUser1 = userFileDAO.getUser("bob"); User realUser2 = userFileDAO.getUser("admin"); @@ -66,14 +67,14 @@ public class UserFileDAOTest { } @Test - public void FindUsersNullTest() throws IOException { + public void findUsersNullTest() { User fakeUser = userFileDAO.getUser("phil.n.thropist"); assertNull(fakeUser); } @Test - public void CreateUserTest() throws IOException { + public void createUserTest() throws IOException { User newUser = new User("keshey"); userFileDAO.addUser(newUser); @@ -84,7 +85,7 @@ public class UserFileDAOTest { } @Test - public void DeleteUserTest() throws IOException { + public void deleteUserTest() throws IOException { User notDeletedUser = userFileDAO.getUser("jelly12"); assertNotNull(notDeletedUser); @@ -96,7 +97,7 @@ public class UserFileDAOTest { } @Test - public void UpdateUserTest() throws IOException { + public void updateUserTest() throws IOException { User toBeUpdatedUser = userFileDAO.getUser("admin"); assertNotNull(toBeUpdatedUser); -- cgit v1.2.3 From 1bf30a02d52bb4f9503e3a5ad9cc52638196a8c4 Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 13 Mar 2025 20:56:21 -0400 Subject: Fix a failing testGetUser() test --- .../com/ufund/api/ufundapi/controller/UserControllerTest.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java index 11fa6a4..e2c959a 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java @@ -8,6 +8,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -24,12 +26,11 @@ import com.ufund.api.ufundapi.service.UserService; public class UserControllerTest { private UserController userController; private UserService mockUserService; - private AuthService mockAuthService; @BeforeEach public void setupUserController() { mockUserService = mock(UserService.class); - mockAuthService = mock(AuthService.class); + AuthService mockAuthService = mock(AuthService.class); userController = new UserController(mockUserService, mockAuthService); } @@ -48,7 +49,8 @@ public class UserControllerTest { // Analyze assertEquals(HttpStatus.OK, response.getStatusCode()); - assertEquals(user, response.getBody()); + assertNotNull(response.getBody()); + assertEquals(user.getUsername(), response.getBody().getUsername()); } @Test -- cgit v1.2.3 From b1798a47cdceaa6d57f211acd5c0b386598af913 Mon Sep 17 00:00:00 2001 From: sowgro Date: Thu, 13 Mar 2025 22:34:29 -0400 Subject: fix pom --- ufund-api/pom.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ufund-api/pom.xml b/ufund-api/pom.xml index d874a29..d2e8fb8 100644 --- a/ufund-api/pom.xml +++ b/ufund-api/pom.xml @@ -73,8 +73,10 @@ jacoco-maven-plugin ${jacoco.version} - /target/coverage-reports/jacoco-unit.exec - /target/coverage-reports/jacoco-unit.exec + + + -- cgit v1.2.3 From a50b260a6f33dbe78e7ac2aa80011b0a2397f3bc Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Fri, 14 Mar 2025 19:50:10 -0400 Subject: Fixed imports --- .../api/ufundapi/persistence/CupboardFileDAOTest.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java index cbfa30c..7888084 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java @@ -1,23 +1,21 @@ package com.ufund.api.ufundapi.persistence; +import java.io.File; +import java.io.IOException; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.io.File; -import java.io.IOException; - import com.fasterxml.jackson.databind.ObjectMapper; import com.ufund.api.ufundapi.model.Need; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - import com.ufund.api.ufundapi.model.Need.GoalType; @Tag("Persistence-tier") -- cgit v1.2.3 From 51f0322db803ed3baf1f24f18a6e7a83dab58a3b Mon Sep 17 00:00:00 2001 From: sowgro Date: Sat, 15 Mar 2025 17:28:01 -0400 Subject: Add login redirection --- ufund-ui/src/app/app.component.ts | 10 ++++++---- ufund-ui/src/app/app.module.ts | 6 +++++- .../app/components/dashboard/dashboard.component.html | 6 ++++-- .../funding-basket/funding-basket.component.ts | 16 ++++++++++++++-- .../src/app/components/login/login.component.html | 1 + ufund-ui/src/app/components/login/login.component.ts | 19 ++++++++++++++----- ufund-ui/src/app/services/users.service.ts | 10 +++++++--- 7 files changed, 51 insertions(+), 17 deletions(-) diff --git a/ufund-ui/src/app/app.component.ts b/ufund-ui/src/app/app.component.ts index a85d04b..6f4e1f5 100644 --- a/ufund-ui/src/app/app.component.ts +++ b/ufund-ui/src/app/app.component.ts @@ -1,7 +1,6 @@ import {Component, OnInit} from '@angular/core'; import {UsersService} from './services/users.service'; -import {BehaviorSubject, Observable, Subject} from 'rxjs'; -import {User} from './models/User'; +import {BehaviorSubject} from 'rxjs'; @Component({ selector: 'app-root', @@ -18,8 +17,11 @@ export class AppComponent implements OnInit { ) {} ngOnInit() { - this.userService.getCurrentUser().subscribe(r => { - this.currentUser$?.next("Logged in as " + r.username) + this.userService.getCurrentUserSubject().subscribe(r => { + this.currentUser$?.next(r + ? "Logged in as " + r.username + : "Logged out." + ) }) } diff --git a/ufund-ui/src/app/app.module.ts b/ufund-ui/src/app/app.module.ts index fa54c58..4b50580 100644 --- a/ufund-ui/src/app/app.module.ts +++ b/ufund-ui/src/app/app.module.ts @@ -12,6 +12,8 @@ import {HttpClientModule} from '@angular/common/http'; import {FormsModule} from '@angular/forms'; import {RouterLink, RouterLinkActive, RouterOutlet} from '@angular/router'; import {DashboardComponent} from './components/dashboard/dashboard.component'; +import {CommonModule} from '@angular/common'; +import {LoginComponent} from './components/login/login.component'; @NgModule({ declarations: [ @@ -21,7 +23,8 @@ import {DashboardComponent} from './components/dashboard/dashboard.component'; FundingBasketComponent, CupboardComponent, NeedListComponent, - DashboardComponent + DashboardComponent, + LoginComponent ], imports: [ BrowserModule, @@ -30,6 +33,7 @@ import {DashboardComponent} from './components/dashboard/dashboard.component'; RouterLink, RouterLinkActive, RouterOutlet, + CommonModule, HttpClientModule, ], providers: [], diff --git a/ufund-ui/src/app/components/dashboard/dashboard.component.html b/ufund-ui/src/app/components/dashboard/dashboard.component.html index f41ccef..c73849f 100644 --- a/ufund-ui/src/app/components/dashboard/dashboard.component.html +++ b/ufund-ui/src/app/components/dashboard/dashboard.component.html @@ -1,3 +1,5 @@

dashboard works!

-Go to the Cupboard -Go to my basket + diff --git a/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts b/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts index 8b12306..c44aa27 100644 --- a/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts +++ b/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts @@ -1,4 +1,6 @@ -import { Component } from '@angular/core'; +import {Component, OnInit} from '@angular/core'; +import {Router} from '@angular/router'; +import {UsersService} from '../../services/users.service'; @Component({ selector: 'app-funding-basket', @@ -6,6 +8,16 @@ import { Component } from '@angular/core'; templateUrl: './funding-basket.component.html', styleUrl: './funding-basket.component.css' }) -export class FundingBasketComponent { +export class FundingBasketComponent implements OnInit{ + constructor( + private router: Router, + private userService: UsersService + ) {} + + ngOnInit() { + if (!this.userService.getCurrentUser()) { + this.router.navigate(['/login'], {queryParams: {redir: this.router.url}}) + } + } } diff --git a/ufund-ui/src/app/components/login/login.component.html b/ufund-ui/src/app/components/login/login.component.html index 178ddbf..bfd7f5e 100644 --- a/ufund-ui/src/app/components/login/login.component.html +++ b/ufund-ui/src/app/components/login/login.component.html @@ -1,3 +1,4 @@ +
You must be logged in to view this page

Login:

diff --git a/ufund-ui/src/app/components/login/login.component.ts b/ufund-ui/src/app/components/login/login.component.ts index 50dd018..7d90624 100644 --- a/ufund-ui/src/app/components/login/login.component.ts +++ b/ufund-ui/src/app/components/login/login.component.ts @@ -1,6 +1,6 @@ -import { Component } from '@angular/core' +import {Component, OnInit} from '@angular/core' import {UsersService} from '../../services/users.service'; -import {Router} from '@angular/router'; +import {ActivatedRoute, Router} from '@angular/router'; @Component({ selector: 'app-login', @@ -8,20 +8,29 @@ import {Router} from '@angular/router'; templateUrl: './login.component.html', styleUrl: './login.component.css' }) -export class LoginComponent { +export class LoginComponent implements OnInit { + + protected next?: string | null; + constructor( protected usersService: UsersService, - private router: Router + private router: Router, + private route: ActivatedRoute ) {} + ngOnInit() { + this.next = this.route.snapshot.queryParamMap.get('redir') + } + login(username: string | null, password: string | null) { + let next = this.next || '/dashboard' console.log(`attempting to log in with ${username} ${password}`) if (!username || !password) { return; } this.usersService.login(username, password).then(() => { - this.router.navigate(['/dashboard']); + this.router.navigate([next]); }) } } diff --git a/ufund-ui/src/app/services/users.service.ts b/ufund-ui/src/app/services/users.service.ts index 28cc266..b3bbbd4 100644 --- a/ufund-ui/src/app/services/users.service.ts +++ b/ufund-ui/src/app/services/users.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import {HttpClient, HttpHeaders} from '@angular/common/http'; -import {firstValueFrom, Observable, of, Subject} from 'rxjs'; +import {BehaviorSubject, firstValueFrom, Observable} from 'rxjs'; import {User} from '../models/User'; @Injectable({ @@ -8,7 +8,7 @@ import {User} from '../models/User'; }) export class UsersService { - private currentUser : Subject = new Subject(); + private currentUser : BehaviorSubject = new BehaviorSubject(null); private apiKey: string = ""; private url = "http://localhost:8080/users" @@ -48,10 +48,14 @@ export class UsersService { return this.http.delete(`${this.url}/${id}`, this.httpOptions) } - getCurrentUser() { + getCurrentUserSubject() { return this.currentUser; } + getCurrentUser() { + return this.currentUser.getValue() + } + async login(username: string, password: string) { let res = this.http.post(this.authUrl, {username: username, password: password}, this.httpOptions2); this.apiKey = await firstValueFrom(res); -- cgit v1.2.3 From d97d4d430113088c4f52f53c040d8705c66b410e Mon Sep 17 00:00:00 2001 From: sowgro Date: Sat, 15 Mar 2025 18:34:58 -0400 Subject: Add support for account creation --- ufund-ui/src/app/app.module.ts | 2 +- .../src/app/components/login/login.component.css | 12 +++- .../src/app/components/login/login.component.html | 5 +- .../src/app/components/login/login.component.ts | 66 +++++++++++++++++++++- ufund-ui/src/app/services/users.service.ts | 4 +- 5 files changed, 82 insertions(+), 7 deletions(-) diff --git a/ufund-ui/src/app/app.module.ts b/ufund-ui/src/app/app.module.ts index 4b50580..9f525fe 100644 --- a/ufund-ui/src/app/app.module.ts +++ b/ufund-ui/src/app/app.module.ts @@ -24,7 +24,7 @@ import {LoginComponent} from './components/login/login.component'; CupboardComponent, NeedListComponent, DashboardComponent, - LoginComponent + LoginComponent, ], imports: [ BrowserModule, diff --git a/ufund-ui/src/app/components/login/login.component.css b/ufund-ui/src/app/components/login/login.component.css index afd4bf1..435cc87 100644 --- a/ufund-ui/src/app/components/login/login.component.css +++ b/ufund-ui/src/app/components/login/login.component.css @@ -1,6 +1,16 @@ -:host { +:host, .border { display: flex; flex-direction: column; max-width: 300px; gap: 5px } + +.border { + border-style: solid; + border-width: 1px; + padding: 10px; + margin: 10px; + position: absolute; + background-color: white; + box-shadow: 0 0 10px 10px black; +} diff --git a/ufund-ui/src/app/components/login/login.component.html b/ufund-ui/src/app/components/login/login.component.html index bfd7f5e..2cdb6d0 100644 --- a/ufund-ui/src/app/components/login/login.component.html +++ b/ufund-ui/src/app/components/login/login.component.html @@ -1,6 +1,7 @@ -
You must be logged in to view this page
+You must be logged in to view this page

Login:

- + +{{statusText | async}} diff --git a/ufund-ui/src/app/components/login/login.component.ts b/ufund-ui/src/app/components/login/login.component.ts index 7d90624..9d806f5 100644 --- a/ufund-ui/src/app/components/login/login.component.ts +++ b/ufund-ui/src/app/components/login/login.component.ts @@ -1,6 +1,7 @@ import {Component, OnInit} from '@angular/core' import {UsersService} from '../../services/users.service'; import {ActivatedRoute, Router} from '@angular/router'; +import {BehaviorSubject} from 'rxjs'; @Component({ selector: 'app-login', @@ -11,10 +12,11 @@ import {ActivatedRoute, Router} from '@angular/router'; export class LoginComponent implements OnInit { protected next?: string | null; + protected statusText = new BehaviorSubject("") constructor( protected usersService: UsersService, - private router: Router, + protected router: Router, private route: ActivatedRoute ) {} @@ -31,6 +33,68 @@ export class LoginComponent implements OnInit { this.usersService.login(username, password).then(() => { this.router.navigate([next]); + }).catch(ex => { + this.statusText.next("Unable to login: " + friendlyHttpStatus[ex.status]) + console.log(ex) + }) + } + + signup(username: string | null, password: string | null) { + console.log(`attempting to sign up with ${username} ${password}`) + if (!username || !password) { + return; + } + + this.usersService.createUser(username, password).then(() => { + this.statusText.next("Account created, click login.") + }).catch(ex => { + this.statusText.next("Unable to create account: " + friendlyHttpStatus[ex.status]) + console.log(ex) }) } } + +// temporary +let friendlyHttpStatus: {[key: number]: string} = { + 200: 'OK', + 201: 'Created', + 202: 'Accepted', + 203: 'Non-Authoritative Information', + 204: 'No Content', + 205: 'Reset Content', + 206: 'Partial Content', + 300: 'Multiple Choices', + 301: 'Moved Permanently', + 302: 'Found', + 303: 'See Other', + 304: 'Not Modified', + 305: 'Use Proxy', + 306: 'Unused', + 307: 'Temporary Redirect', + 400: 'Bad Request', + 401: 'Unauthorized', + 402: 'Payment Required', + 403: 'Forbidden', + 404: 'Not Found', + 405: 'Method Not Allowed', + 406: 'Not Acceptable', + 407: 'Proxy Authentication Required', + 408: 'Request Timeout', + 409: 'Conflict', + 410: 'Gone', + 411: 'Length Required', + 412: 'Precondition Required', + 413: 'Request Entry Too Large', + 414: 'Request-URI Too Long', + 415: 'Unsupported Media Type', + 416: 'Requested Range Not Satisfiable', + 417: 'Expectation Failed', + 418: 'I\'m a teapot', + 429: 'Too Many Requests', + 500: 'Internal Server Error', + 501: 'Not Implemented', + 502: 'Bad Gateway', + 503: 'Service Unavailable', + 504: 'Gateway Timeout', + 505: 'HTTP Version Not Supported', +}; diff --git a/ufund-ui/src/app/services/users.service.ts b/ufund-ui/src/app/services/users.service.ts index b3bbbd4..c570ccf 100644 --- a/ufund-ui/src/app/services/users.service.ts +++ b/ufund-ui/src/app/services/users.service.ts @@ -32,8 +32,8 @@ export class UsersService { private http: HttpClient ) {} - createUser(data: User): Observable { - return this.http.post(this.url, data, this.httpOptions) + async createUser(username:string, password:string) { + await firstValueFrom(this.http.post(this.url, {username: username, password: password}, this.httpOptions)) } getUser(id: string): Observable { -- cgit v1.2.3 From 4ac7711c4d9dd3275ae4037f843347e4fbcb1f2a Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Sat, 15 Mar 2025 23:18:54 -0400 Subject: Added additional check to createNeed method --- .../com/ufund/api/ufundapi/service/CupboardService.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java index c8609ab..6dd120c 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java @@ -3,10 +3,11 @@ package com.ufund.api.ufundapi.service; import java.io.IOException; import java.util.Arrays; -import com.ufund.api.ufundapi.model.Need; -import com.ufund.api.ufundapi.persistence.CupboardDAO; import org.springframework.stereotype.Component; + import com.ufund.api.ufundapi.DuplicateKeyException; +import com.ufund.api.ufundapi.model.Need; +import com.ufund.api.ufundapi.persistence.CupboardDAO; @Component public class CupboardService { @@ -27,16 +28,18 @@ public class CupboardService { * @throws IOException Thrown if there was any issue saving the data * @throws DuplicateKeyException If there already exists a need with the same name */ - public Need createNeed(String name, int maxGoal, Need.GoalType goalType) throws IOException, DuplicateKeyException { + public Need createNeed(String name, double maxGoal, Need.GoalType goalType) throws IOException, DuplicateKeyException { Need need = new Need(name, goalType, maxGoal); if (need.getMaxGoal() <= 0) { throw new IllegalArgumentException("Max Goal must be greater than zero"); } else { - for (Need searchNeed : cupboardDAO.getNeeds()) { - if (need.getName().equalsIgnoreCase(searchNeed.getName())) { - throw new DuplicateKeyException("Duplicate names are not allowed"); + if (cupboardDAO.getNeeds().length > 0) { + for (Need searchNeed : cupboardDAO.getNeeds()) { + if (need.getName().equalsIgnoreCase(searchNeed.getName())) { + throw new DuplicateKeyException("Duplicate names are not allowed"); + } } } return cupboardDAO.addNeed(need); -- cgit v1.2.3 From 5117f5eace7c6c905b5c1cf5871a3dbd29d67d5c Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Sat, 15 Mar 2025 23:19:11 -0400 Subject: Updated tests to use new need declaration --- .../java/com/ufund/api/ufundapi/model/NeedTest.java | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/model/NeedTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/model/NeedTest.java index 772af5c..6b4ddfc 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/model/NeedTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/model/NeedTest.java @@ -14,13 +14,10 @@ public class NeedTest { public void createNeed() { String name = "Jellyfish"; - int id = 0; double maxGoal = 100.00; GoalType type = GoalType.MONETARY; - Need need = new Need(name, id, maxGoal, type); - + Need need = new Need(name, type, maxGoal); assertNotNull(need); - } @Test @@ -29,7 +26,7 @@ public class NeedTest { int id = 0; double maxGoal = 100.00; GoalType type = GoalType.MONETARY; - Need need = new Need(name, id, maxGoal, type); + Need need = new Need(name, type, maxGoal); assertEquals(name, need.getName()); @@ -41,10 +38,9 @@ public class NeedTest { @Test public void testCurrentGoal() { String name = "Jellyfish"; - int id = 0; double maxGoal = 100.00; GoalType type = GoalType.MONETARY; - Need need = new Need(name, id, maxGoal, type); + Need need = new Need(name, type, maxGoal); double current = 0.00; need.setCurrent(current); @@ -64,10 +60,9 @@ public class NeedTest { public void testFilterAttributes() { String name = "Jellyfish"; - int id = 0; double maxGoal = 100.00; GoalType type = GoalType.MONETARY; - Need need = new Need(name, id, maxGoal, type); + Need need = new Need(name, type, maxGoal); String[] filterAttributes = {"seaweed", "divers", "pacific", "plankton"}; @@ -80,10 +75,9 @@ public class NeedTest { public void testSetMaxGoal() { String name = "Jellyfish"; - int id = 0; double maxGoal = 100.00; GoalType type = GoalType.MONETARY; - Need need = new Need(name, id, maxGoal, type); + Need need = new Need(name, type, maxGoal); double newGoal = 200.00; need.setMaxGoal(newGoal); @@ -96,10 +90,9 @@ public class NeedTest { public void testSetName() { String name = "Jellyfish"; - int id = 0; double maxGoal = 100.00; GoalType type = GoalType.MONETARY; - Need need = new Need(name, id, maxGoal, type); + Need need = new Need(name, type, maxGoal); String newName = "TESTINGFUN"; need.setName(newName); -- cgit v1.2.3 From 28e46060c8bcf7fd2adc19793bd63e27df4e7356 Mon Sep 17 00:00:00 2001 From: Gunther6070 Date: Sat, 15 Mar 2025 23:19:53 -0400 Subject: Created class and began implementing createNeed test, but having issues with mocking --- .../api/ufundapi/service/CupboardServiceTest.java | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 ufund-api/src/test/java/com/ufund/api/ufundapi/service/CupboardServiceTest.java diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/service/CupboardServiceTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/service/CupboardServiceTest.java new file mode 100644 index 0000000..09d7c89 --- /dev/null +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/service/CupboardServiceTest.java @@ -0,0 +1,57 @@ +package com.ufund.api.ufundapi.service; + +import java.io.IOException; +import java.util.TreeMap; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.ufund.api.ufundapi.DuplicateKeyException; +import com.ufund.api.ufundapi.model.Need; +import com.ufund.api.ufundapi.model.Need.GoalType; +import com.ufund.api.ufundapi.persistence.CupboardDAO; + +@Tag("Service-tier") +public class CupboardServiceTest { + + private CupboardDAO mockCupboardDAO; + private AuthService mockAuthService; + private CupboardService cupboardService; + + @BeforeEach + public void setupCupboardService() throws IOException { + mockCupboardDAO = mock(CupboardDAO.class); + mockAuthService = mock(AuthService.class); + cupboardService = new CupboardService(mockCupboardDAO); + + } + + @Test + public void testCreateNeed() throws IOException, DuplicateKeyException { + // Setup + String name = "Jellyfish"; + double maxGoal = 100.00; + int id = 0; + GoalType type = GoalType.MONETARY; + Need need = new Need(name, type, maxGoal); + + // When the same id is passed in, our mock User DAO will return the User object + when(mockCupboardDAO.getNeed(id)).thenReturn(need); + when(mockCupboardDAO.addNeed(need)).thenReturn(need); + when(mockCupboardDAO.getNeeds()).thenReturn(new Need[0]); + + + // Invoke + Need response = cupboardService.createNeed(name, maxGoal, type); + + // Analyze + assertNotNull(response); + assertEquals(need, response); + } + +} -- cgit v1.2.3 From a3150b8a8e17c8a71f617745bb8588b397a75f47 Mon Sep 17 00:00:00 2001 From: sowgro Date: Sat, 15 Mar 2025 23:52:58 -0400 Subject: fix testCreateNeed() --- .../api/ufundapi/service/CupboardService.java | 22 ++++++++++------------ .../api/ufundapi/service/CupboardServiceTest.java | 9 ++++----- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java index 6dd120c..78f8f85 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java @@ -29,22 +29,20 @@ public class CupboardService { * @throws DuplicateKeyException If there already exists a need with the same name */ public Need createNeed(String name, double maxGoal, Need.GoalType goalType) throws IOException, DuplicateKeyException { - - Need need = new Need(name, goalType, maxGoal); - if (need.getMaxGoal() <= 0) { + if (maxGoal <= 0) { throw new IllegalArgumentException("Max Goal must be greater than zero"); - } else { - if (cupboardDAO.getNeeds().length > 0) { - for (Need searchNeed : cupboardDAO.getNeeds()) { - if (need.getName().equalsIgnoreCase(searchNeed.getName())) { - throw new DuplicateKeyException("Duplicate names are not allowed"); - } - } + } + + for (Need searchNeed : cupboardDAO.getNeeds()) { + if (searchNeed.getName().equalsIgnoreCase(name)) { + throw new DuplicateKeyException("Duplicate names are not allowed"); } - return cupboardDAO.addNeed(need); } - + + Need need = new Need(name, goalType, maxGoal); + return cupboardDAO.addNeed(need); + } /** diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/service/CupboardServiceTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/service/CupboardServiceTest.java index 09d7c89..ceef215 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/service/CupboardServiceTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/service/CupboardServiceTest.java @@ -1,15 +1,14 @@ package com.ufund.api.ufundapi.service; import java.io.IOException; -import java.util.TreeMap; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.*; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import com.ufund.api.ufundapi.DuplicateKeyException; import com.ufund.api.ufundapi.model.Need; @@ -24,7 +23,7 @@ public class CupboardServiceTest { private CupboardService cupboardService; @BeforeEach - public void setupCupboardService() throws IOException { + public void setupCupboardService() { mockCupboardDAO = mock(CupboardDAO.class); mockAuthService = mock(AuthService.class); cupboardService = new CupboardService(mockCupboardDAO); @@ -42,7 +41,7 @@ public class CupboardServiceTest { // When the same id is passed in, our mock User DAO will return the User object when(mockCupboardDAO.getNeed(id)).thenReturn(need); - when(mockCupboardDAO.addNeed(need)).thenReturn(need); + when(mockCupboardDAO.addNeed(any())).thenReturn(need); when(mockCupboardDAO.getNeeds()).thenReturn(new Need[0]); -- cgit v1.2.3