aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHayden Hartman <haydenhartman10@gmail.com>2025-03-15 23:59:47 -0400
committerGitHub <noreply@github.com>2025-03-15 23:59:47 -0400
commit9baaa0590fbc38c06d530786a1de804ee9edd7db (patch)
tree7c94dc98f9b1978f8ccf3c38bb3777237bf0788a
parente4e6ae9a3d142fc78f31ee19464ec5e54bfb516f (diff)
parenta3150b8a8e17c8a71f617745bb8588b397a75f47 (diff)
downloadJellySolutions-9baaa0590fbc38c06d530786a1de804ee9edd7db.tar.gz
JellySolutions-9baaa0590fbc38c06d530786a1de804ee9edd7db.tar.bz2
JellySolutions-9baaa0590fbc38c06d530786a1de804ee9edd7db.zip
Merge pull request #8 from RIT-SWEN-261-02/api-auth
First attempt at an authentication system.
-rw-r--r--ufund-api/data/cupboard.json9
-rw-r--r--ufund-api/data/userAuths.json1
-rw-r--r--ufund-api/data/users.json13
-rw-r--r--ufund-api/pom.xml6
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/DuplicateKeyException.java7
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java62
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java70
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java81
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/model/Need.java13
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/model/User.java39
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/model/UserAuth.java43
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardDAO.java21
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDAO.java (renamed from ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDao.java)25
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthDAO.java32
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java73
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java21
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java88
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java63
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java106
-rw-r--r--ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java72
-rw-r--r--ufund-api/src/main/resources/application.properties1
-rw-r--r--ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java114
-rw-r--r--ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java105
-rw-r--r--ufund-api/src/test/java/com/ufund/api/ufundapi/model/NeedTest.java25
-rw-r--r--ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java11
-rw-r--r--ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDAOTest.java (renamed from ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/CupboardFileDaoTest.java)45
-rw-r--r--ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java43
-rw-r--r--ufund-api/src/test/java/com/ufund/api/ufundapi/service/CupboardServiceTest.java56
-rw-r--r--ufund-ui/src/app/app-routing.module.ts2
-rw-r--r--ufund-ui/src/app/app.component.html4
-rw-r--r--ufund-ui/src/app/app.component.ts23
-rw-r--r--ufund-ui/src/app/app.module.ts14
-rw-r--r--ufund-ui/src/app/components/dashboard/dashboard.component.html4
-rw-r--r--ufund-ui/src/app/components/dashboard/dashboard.component.ts2
-rw-r--r--ufund-ui/src/app/components/funding-basket/funding-basket.component.ts16
-rw-r--r--ufund-ui/src/app/components/login/login.component.css12
-rw-r--r--ufund-ui/src/app/components/login/login.component.html10
-rw-r--r--ufund-ui/src/app/components/login/login.component.ts93
-rw-r--r--ufund-ui/src/app/models/Need.ts2
-rw-r--r--ufund-ui/src/app/models/User.ts2
-rw-r--r--ufund-ui/src/app/services/cupboard.service.ts3
-rw-r--r--ufund-ui/src/app/services/users.service.ts48
42 files changed, 1099 insertions, 381 deletions
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..41ff472
--- /dev/null
+++ b/ufund-api/data/userAuths.json
@@ -0,0 +1 @@
+[{"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/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/pom.xml b/ufund-api/pom.xml
index ce96d60..d2e8fb8 100644
--- a/ufund-api/pom.xml
+++ b/ufund-api/pom.xml
@@ -73,8 +73,10 @@
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<configuration>
- <destfile>/target/coverage-reports/jacoco-unit.exec</destfile>
- <datafile>/target/coverage-reports/jacoco-unit.exec</datafile>
+ <!-- not sure what these were supposed to do, but 'datafile' is not a
+ configuration key for jacoco, and changing it to 'dataFile' breaks the build -->
+ <!-- <datafile>/target/coverage-reports/jacoco-unit.exec</datafile>-->
+ <!-- <datafile>/target/coverage-reports/jacoco-unit.exec</datafile>-->
</configuration>
<executions>
<execution>
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/AuthController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java
new file mode 100644
index 0000000..b46d4ee
--- /dev/null
+++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java
@@ -0,0 +1,62 @@
+package com.ufund.api.ufundapi.controller;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+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 com.ufund.api.ufundapi.service.AuthService;
+
+@RestController
+@RequestMapping("auth")
+public class AuthController {
+ private final AuthService authService;
+
+ public AuthController(AuthService authService) {
+ this.authService = authService;
+ }
+
+ /**
+ * Attempts to log in as a user
+ *
+ * @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<String> login(@RequestBody Map<String, String> params) {
+ String username = params.get("username");
+ String password = params.get("password");
+ try {
+ String key = authService.login(username, password);
+ return new ResponseEntity<>(key, HttpStatus.OK);
+ } catch (IllegalAccessException e) {
+ return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
+ } catch (IOException ex) {
+ return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * 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<Object> 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 faaa98a..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,8 @@
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;
@@ -17,37 +19,47 @@ 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.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;
}
/**
* 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<Need> createNeed(@RequestBody Need need) {
+ public ResponseEntity<Need> createNeed(@RequestBody Map<String, String> params) {
+ String name = params.get("name");
+ int maxGoal = Integer.parseInt(params.get("maxGoal"));
+ Need.GoalType goalType = GoalType.valueOf(params.get("goalType"));
+
try {
- if (need.getMaxGoal() <= 0) {
- 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);
}
@@ -66,7 +78,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());
@@ -90,8 +102,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);
@@ -103,23 +115,20 @@ 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<br>
+ * @return ResponseEntity with {@link Need need} object and HTTP status of OK if found<br>
* ResponseEntity with HTTP status of NOT_FOUND if not found<br>
- * ResponseEntity with HTTP status of INTERNAL_SERVER_ERROR otherwise
*/
@GetMapping("/{id}")
public ResponseEntity<Need> getNeed(@PathVariable int id) {
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 {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
-
} catch (IOException e) {
LOG.log(Level.SEVERE, e.getLocalizedMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
@@ -133,12 +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<Need> updateNeed(@RequestBody Need need) {
+ @PutMapping("/{id}")
+ public ResponseEntity<Need> updateNeed(@RequestBody Need need, @PathVariable int id) {
try {
- need = cupboardDAO.updateNeed(need);
- return new ResponseEntity<>(need, HttpStatus.OK);
+ 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);
}
@@ -153,9 +167,9 @@ public class CupboardController {
@DeleteMapping("/{id}")
public ResponseEntity<Need> 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/controller/UserController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java
index 4e5f156..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
@@ -1,6 +1,8 @@
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;
@@ -12,43 +14,47 @@ 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.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(CupboardController.class.getName());
- private final UserDAO UserDAO;
+ private static final Logger LOG = Logger.getLogger(UserController.class.getName());
+ private final UserService userService;
+ private final AuthService authService;
- /**
- * Create a user controller to receive REST signals
- *
- * @param userDAO The Data Access Object
- */
- public UserController(UserDAO userDAO) {
- this.UserDAO = userDAO;
+ public UserController(UserService userService, AuthService authService) {
+ this.userService = userService;
+ this.authService = authService;
}
/**
* Creates a User with the provided object
- *
- * @param user The user to create
+ * @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
*/
@PostMapping("")
- public ResponseEntity<User> createUser(@RequestBody User user) {
+ public ResponseEntity<User> createUser(@RequestBody Map<String, String> params) {
+ String username = params.get("username");
+ String password = params.get("password");
+
try {
- if (UserDAO.createUser(user) != null) {
+ User user = userService.createUser(username, password);
+ if (user != null) {
return new ResponseEntity<>(user, HttpStatus.CREATED);
} else {
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
-
+ } catch (DuplicateKeyException ex) {
+ return new ResponseEntity<>(HttpStatus.CONFLICT);
} catch (IOException ex) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
@@ -57,23 +63,27 @@ 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<br>
* ResponseEntity with HTTP status of NOT_FOUND if not found<br>
* ResponseEntity with HTTP status of INTERNAL_SERVER_ERROR otherwise
*/
- @GetMapping("/{name}")
- public ResponseEntity<User> getUser(@PathVariable String name) {
- LOG.log(Level.INFO, "GET /user/{0}", name);
+ @GetMapping("/{username}")
+ public ResponseEntity<User> getUser(@PathVariable String username, @RequestHeader("jelly-api-key") String key) {
+ LOG.log(Level.INFO, "GET /user/{0}", username);
try {
- User user = UserDAO.getUser(name);
+ authService.authenticate(username, key);
+ User user = userService.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);
}
-
+ } catch (IllegalAccessException ex) {
+ return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
} catch (IOException e) {
LOG.log(Level.SEVERE, e.getLocalizedMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
@@ -84,42 +94,53 @@ 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
*/
- @PutMapping("/{name}")
- public ResponseEntity<User> updateUser(@RequestBody User user, @PathVariable String name) {
+ @PutMapping("/{username}")
+ public ResponseEntity<User> 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 {
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) {
+ return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
}
/**
* Deletes a user with the desired name
*
- * @param name The name of the user
+ * @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
*/
- @DeleteMapping("/{name}")
- public ResponseEntity<User> deleteUser(@PathVariable String name) {
+ @DeleteMapping("/{username}")
+ public ResponseEntity<Boolean> deleteUser(@PathVariable String username, @RequestHeader("jelly-api-key") String key) {
+
try {
- if (UserDAO.deleteUser(name)) {
+ 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/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
@@ -32,6 +32,19 @@ public class Need {
}
/**
+ * 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
*
* @param other The need to copy from
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<Need> basket) {
- this.name = name;
+ public User(@JsonProperty("username") String username, @JsonProperty("passwordHash") int passwordHash, @JsonProperty("basket") List<Need> 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/CupboardDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardDAO.java
index 1435410..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
@@ -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
*
@@ -14,23 +14,10 @@ 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;
/**
- * 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
*
* @param id The ID of the {@link Need need} to get
@@ -38,8 +25,6 @@ public interface CupboardDAO {
* @return a {@link Need need} object with the matching name
* <br>
* 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;
@@ -54,7 +39,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..c4aaca3 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
@@ -11,14 +11,14 @@ import java.util.Map;
import java.util.TreeMap;
@Component
-public class CupboardFileDao implements CupboardDAO {
+public class CupboardFileDAO implements CupboardDAO {
private final Map<Integer, Need> 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 {
+ public CupboardFileDAO(@Value("${cupboard.file}") String filename, ObjectMapper objectMapper) throws IOException {
this.filename = filename;
this.objectMapper = objectMapper;
needs = new TreeMap<>();
@@ -61,18 +61,6 @@ public class CupboardFileDao implements CupboardDAO {
}
/**
- * 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
*
* @return True if the save was successful, false otherwise
@@ -93,13 +81,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) {
return needs.getOrDefault(id, null);
@@ -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/persistence/UserAuthDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthDAO.java
new file mode 100644
index 0000000..355aae4
--- /dev/null
+++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthDAO.java
@@ -0,0 +1,32 @@
+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) throws IOException;
+
+ /**
+ * Add a user authentication profile
+ *
+ * @param userAuth The user auth profile to add
+ * @throws IOException Thrown on any file writing error
+ */
+ 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
new file mode 100644
index 0000000..1fc1e92
--- /dev/null
+++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java
@@ -0,0 +1,73 @@
+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<String, UserAuth> 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();
+ }
+
+ /**
+ * 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();
+
+ UserAuth[] userAuthKeysArray = objectMapper.readValue(new File(filename), UserAuth[].class);
+
+ for (UserAuth userAuth : userAuthKeysArray) {
+ userAuthMap.put(userAuth.getKey(), userAuth);
+ }
+ }
+
+ /**
+ * 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());
+ }
+
+ @Override
+ public UserAuth getUserAuth(String key) {
+ synchronized (userAuthMap) {
+ return userAuthMap.get(key);
+ }
+ }
+
+ @Override
+ public void addUserAuth(UserAuth userAuth) throws IOException {
+ synchronized (userAuthMap) {
+ userAuthMap.put(userAuth.getKey(), userAuth);
+ save();
+ }
+ }
+
+ @Override
+ public void removeUserAuth(String key) throws IOException {
+ synchronized (userAuthMap) {
+ userAuthMap.remove(key);
+ save();
+ }
+ }
+}
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..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
@@ -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
* <br>
- * 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,25 +44,24 @@ 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}
*
- * @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
*
- * @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
* <br>
@@ -70,5 +69,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..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
@@ -2,8 +2,8 @@ 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;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@@ -21,7 +21,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
}
@@ -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);
}
}
@@ -47,100 +47,54 @@ 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);
}
}
- /**
- * Return the user with the String name name or null otherwise
- *
- * @param name 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) {
synchronized (users) {
- return users.getOrDefault(name, null);
+ return users.getOrDefault(username, null);
}
}
- /**
- * 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 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();
+ if (res == null) {
+ return user;
}
+ return res;
}
}
- /**
- * 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 {
+ 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;
}
}
}
- /**
- * Delete a user matching the name
- *
- * @param name 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/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..591d891
--- /dev/null
+++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java
@@ -0,0 +1,63 @@
+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;
+ }
+
+ /**
+ * 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, IOException {
+ 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)) {
+ throw new IllegalAccessException("Unauthorized");
+ }
+ var userAuth = UserAuth.generate(username);
+ userAuthDAO.addUserAuth(userAuth);
+ 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
new file mode 100644
index 0000000..78f8f85
--- /dev/null
+++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java
@@ -0,0 +1,106 @@
+package com.ufund.api.ufundapi.service;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+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 {
+
+ private final CupboardDAO cupboardDAO;
+
+ 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, double maxGoal, Need.GoalType goalType) throws IOException, DuplicateKeyException {
+
+ if (maxGoal <= 0) {
+ throw new IllegalArgumentException("Max Goal must be greater than zero");
+ }
+
+ for (Need searchNeed : cupboardDAO.getNeeds()) {
+ if (searchNeed.getName().equalsIgnoreCase(name)) {
+ throw new DuplicateKeyException("Duplicate names are not allowed");
+ }
+ }
+
+ Need need = new Need(name, goalType, maxGoal);
+ return cupboardDAO.addNeed(need);
+
+ }
+
+ /**
+ * 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();
+ }
+
+ /**
+ * Returns an array of needs filtered by a search
+ *
+ * @param search The search substring
+ * @return The requested array
+ * @throws IOException Thrown if there was any issue saving the data
+ */
+ public Need[] searchNeeds(String search) throws IOException {
+ return Arrays.stream(cupboardDAO.getNeeds())
+ .filter(i -> i.getName().toLowerCase().contains(search.toLowerCase()))
+ .toArray(Need[]::new);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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, int id) throws IOException {
+ if (need.getId() != id) {
+ throw new IllegalArgumentException("ID in URL and body must match");
+ }
+ 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
new file mode 100644
index 0000000..935ee72
--- /dev/null
+++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java
@@ -0,0 +1,72 @@
+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;
+
+@Component
+public class UserService {
+
+ private final UserDAO userDAO;
+
+ public UserService(UserDAO userDao) {
+ this.userDAO = userDao;
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Updates a user
+ *
+ * @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 username) throws IOException {
+ if (!user.getUsername().equals(username)) {
+ throw new IllegalArgumentException("ID in URL and body must match");
+ }
+ return userDAO.updateUser(user);
+ }
+
+ /**
+ * 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);
+ }
+
+}
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 839c518..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,6 +1,8 @@
package com.ufund.api.ufundapi.controller;
import java.io.IOException;
+import java.util.Map;
+import static java.util.Map.entry;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -11,54 +13,74 @@ 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.persistence.CupboardFileDao;
+import com.ufund.api.ufundapi.model.Need.GoalType;
+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, 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);
+
- var res = cupboardController.createNeed(need);
+ Map<String, String> needMap = Map.ofEntries(
+ entry("name", "Test"),
+ entry("maxGoal", "100"),
+ entry("goalType", "MONETARY")
+ );
+
+ var res = cupboardController.createNeed(needMap);
assertEquals(HttpStatus.OK, res.getStatusCode());
assertEquals(need, res.getBody());
}
@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());
+
+ Map<String, String> needMap = Map.ofEntries(
+ entry("name", "Name"),
+ entry("maxGoal", "-100"),
+ entry("goalType", "MONETARY"));
- var res = cupboardController.createNeed(need);
+ var res = cupboardController.createNeed(needMap);
- assertEquals(HttpStatus.BAD_REQUEST, res.getStatusCode());
+ 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());
+
+ Map<String, String> needMap = Map.ofEntries(
+ entry("name", "Name"),
+ entry("maxGoal", "100"),
+ entry("goalType", "MONETARY"));
- var res = cupboardController.createNeed(need);
+ 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(mockCupboardDAO.getNeeds()).thenReturn(new Need[]{need});
+ when(mockCupboardService.getNeeds()).thenReturn(new Need[]{need});
var res = cupboardController.getNeeds();
@@ -67,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();
@@ -77,8 +98,8 @@ public class CupboardControllerTest {
}
@Test
- public void getNeedsEmpty() {
- when(mockCupboardDAO.getNeeds()).thenReturn(new Need[]{});
+ public void getNeedsEmpty() throws IOException {
+ when(mockCupboardService.getNeeds()).thenReturn(new Need[]{});
var res = cupboardController.getNeeds();
@@ -87,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(mockCupboardDAO.findNeeds("Na")).thenReturn(new Need[]{need});
+ when(mockCupboardService.searchNeeds("Na")).thenReturn(new Need[]{need});
var res = cupboardController.searchNeeds("Na");
@@ -98,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");
@@ -108,8 +128,8 @@ public class CupboardControllerTest {
}
@Test
- public void searchNeedsEmpty() {
- when(mockCupboardDAO.findNeeds("Na")).thenReturn(new Need[]{});
+ public void searchNeedsEmpty() throws IOException {
+ when(mockCupboardService.searchNeeds("Na")).thenReturn(new Need[]{});
var res = cupboardController.searchNeeds("Na");
@@ -118,9 +138,9 @@ public class CupboardControllerTest {
}
@Test
- public void getNeed() {
+ public void getNeed() throws IOException {
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());
@@ -129,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());
@@ -139,9 +159,9 @@ public class CupboardControllerTest {
}
@Test
- public void getNeedFail() {
+ public void getNeedFail() throws IOException {
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());
@@ -152,9 +172,9 @@ 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, 1)).thenReturn(need);
- var res = cupboardController.updateNeed(need);
+ var res = cupboardController.updateNeed(need, 1);
assertEquals(HttpStatus.OK, res.getStatusCode());
assertEquals(need, res.getBody());
@@ -163,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());
}
@@ -173,8 +193,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);
@@ -183,8 +203,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);
@@ -194,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);
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..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
@@ -1,30 +1,37 @@
package com.ufund.api.ufundapi.controller;
import java.io.IOException;
+import java.util.Map;
+import static java.util.Map.entry;
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;
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.persistence.UserFileDAO;
+import com.ufund.api.ufundapi.model.UserAuth;
+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 UserService mockUserService;
@BeforeEach
public void setupUserController() {
- mockUserDAO = mock(UserFileDAO.class);
- userController = new UserController(mockUserDAO);
-
+ mockUserService = mock(UserService.class);
+ AuthService mockAuthService = mock(AuthService.class);
+ userController = new UserController(mockUserService, mockAuthService);
}
@Test
@@ -32,27 +39,32 @@ 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);
+ when(mockUserService.getUser(username)).thenReturn(user);
+
// Invoke
- ResponseEntity<User> response = userController.getUser(username);
+ ResponseEntity<User> response = userController.getUser(username, key);
// Analyze
assertEquals(HttpStatus.OK, response.getStatusCode());
- assertEquals(user, response.getBody());
+ assertNotNull(response.getBody());
+ assertEquals(user.getUsername(), response.getBody().getUsername());
}
@Test
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);
+ when(mockUserService.getUser(username)).thenReturn(null);
+
// Invoke
- ResponseEntity<User> response = userController.getUser(username);
+ ResponseEntity<User> response = userController.getUser(username, key);
// Analyze
assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
@@ -62,11 +74,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);
+ doThrow(new IOException()).when(mockUserService).getUser(username);
// Invoke
- ResponseEntity<User> response = userController.getUser(username);
+ ResponseEntity<User> response = userController.getUser(username, key);
// Analyze
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
@@ -78,16 +91,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);
// when createUser is called, return true simulating successful
// creation and save
- when(mockUserDAO.createUser(user)).thenReturn(user);
+ when(mockUserService.createUser(username, password)).thenReturn(user);
+
+ Map<String, String> userMap = Map.ofEntries(
+ entry("username", "Test"),
+ entry("password", "Pass")
+ );
// Invoke
- ResponseEntity<User> response = userController.createUser(user);
+ ResponseEntity<User> response = userController.createUser(userMap);
// Analyze
assertEquals(HttpStatus.CREATED, response.getStatusCode());
@@ -95,32 +114,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.createUser(user)).thenReturn(null);
+ when(mockUserService.createUser(username, password)).thenReturn(null);
+
+ Map<String, String> userMap = Map.ofEntries(
+ entry("username", "Test"),
+ entry("password", "Pass")
+ );
// Invoke
- ResponseEntity<User> response = userController.createUser(user);
+ ResponseEntity<User> 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).createUser(user);
+ doThrow(new IOException()).when(mockUserService).createUser(username, password);
+
+ Map<String, String> userMap = Map.ofEntries(
+ entry("username", "Test"),
+ entry("password", "Pass")
+ );
// Invoke
- ResponseEntity<User> response = userController.createUser(user);
+ ResponseEntity<User> response = userController.createUser(userMap);
// Analyze
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
@@ -131,12 +160,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<User> response = userController.updateUser(user, username);
+ ResponseEntity<User> response = userController.updateUser(user, username, key);
// Analyze
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -148,12 +178,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<User> response = userController.updateUser(user, username);
+ ResponseEntity<User> response = userController.updateUser(user, username, key);
// Analyze
assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
@@ -164,11 +195,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<User> response = userController.updateUser(user, username);
+ ResponseEntity<User> response = userController.updateUser(user, username, key);
// Analyze
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
@@ -178,11 +210,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<User> response = userController.deleteUser(username);
+ ResponseEntity<Boolean> response = userController.deleteUser(username, key);
// Analyze
assertEquals(HttpStatus.OK, response.getStatusCode());
@@ -192,11 +225,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<User> response = userController.deleteUser(username);
+ ResponseEntity<Boolean> response = userController.deleteUser(username, key);
// Analyze
assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
@@ -206,11 +240,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<User> response = userController.deleteUser(username);
+ ResponseEntity<Boolean> response = userController.deleteUser(username, key);
// Analyze
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
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..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,22 +38,21 @@ 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);
- 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());
}
@@ -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);
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 716fbfd..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 {
@@ -23,10 +23,11 @@ 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.getName());
+ assertEquals(expectedName, user.getUsername());
}
@@ -69,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 8aa6fe0..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,29 +1,26 @@
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 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;
+public class CupboardFileDAOTest {
+ CupboardFileDAO cupboardFileDao;
Need[] testNeeds;
ObjectMapper mockObjectMapper;
@@ -39,41 +36,29 @@ public class CupboardFileDaoTest {
when(mockObjectMapper
.readValue(new File("doesnt_matter.txt"),Need[].class))
.thenReturn(testNeeds);
- cupboardFileDao = new CupboardFileDao("doesnt_matter.txt",mockObjectMapper);
+ cupboardFileDao = new CupboardFileDAO("doesnt_matter.txt",mockObjectMapper);
}
@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);
@@ -81,7 +66,7 @@ public class CupboardFileDaoTest {
}
@Test
- public void DeleteNeedTest() throws IOException {
+ public void deleteNeedTest() throws IOException {
Need undeletedNeed = cupboardFileDao.getNeed(0);
assertNotNull(undeletedNeed);
@@ -93,7 +78,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 dfe9b10..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
@@ -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;
@@ -42,7 +40,7 @@ public class UserFileDAOTest {
}
@Test
- public void GetUsersTest() throws IOException {
+ public void getUsersTest() {
User[] users = userFileDAO.getUsers();
assertEquals(users.length,testUsers.length);
@@ -50,16 +48,17 @@ 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())) {
- 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");
@@ -68,25 +67,25 @@ 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.createUser(newUser);
+ userFileDAO.addUser(newUser);
User actualUser = userFileDAO.getUser("keshey");
assertNotNull(actualUser);
- assertEquals(actualUser.getName(), newUser.getName());
+ assertEquals(actualUser.getUsername(), newUser.getUsername());
}
@Test
- public void DeleteUserTest() throws IOException {
+ public void deleteUserTest() throws IOException {
User notDeletedUser = userFileDAO.getUser("jelly12");
assertNotNull(notDeletedUser);
@@ -98,15 +97,15 @@ public class UserFileDAOTest {
}
@Test
- public void UpdateUserTest() throws IOException {
+ public void updateUserTest() throws IOException {
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.getName());
+ assertEquals("admin", updatedUser.getUsername());
}
}
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..ceef215
--- /dev/null
+++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/service/CupboardServiceTest.java
@@ -0,0 +1,56 @@
+package com.ufund.api.ufundapi.service;
+
+import java.io.IOException;
+
+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 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() {
+ 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(any())).thenReturn(need);
+ when(mockCupboardDAO.getNeeds()).thenReturn(new Need[0]);
+
+
+ // Invoke
+ Need response = cupboardService.createNeed(name, maxGoal, type);
+
+ // Analyze
+ assertNotNull(response);
+ assertEquals(need, response);
+ }
+
+}
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.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 @@
-<h1>jelly solutions:</h1>
+<h1>jelly solutions</h1>
+<span>{{currentUser$ | async}}</span>
+<hr>
<router-outlet />
diff --git a/ufund-ui/src/app/app.component.ts b/ufund-ui/src/app/app.component.ts
index 2dbf33c..6f4e1f5 100644
--- a/ufund-ui/src/app/app.component.ts
+++ b/ufund-ui/src/app/app.component.ts
@@ -1,4 +1,6 @@
-import { Component } from '@angular/core';
+import {Component, OnInit} from '@angular/core';
+import {UsersService} from './services/users.service';
+import {BehaviorSubject} from 'rxjs';
@Component({
selector: 'app-root',
@@ -6,6 +8,21 @@ 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<string> = new BehaviorSubject<string>("Logged out.");
+
+ constructor(
+ private userService: UsersService
+ ) {}
+
+ ngOnInit() {
+ 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 d818841..9f525fe 100644
--- a/ufund-ui/src/app/app.module.ts
+++ b/ufund-ui/src/app/app.module.ts
@@ -9,6 +9,11 @@ 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';
+import {DashboardComponent} from './components/dashboard/dashboard.component';
+import {CommonModule} from '@angular/common';
+import {LoginComponent} from './components/login/login.component';
@NgModule({
declarations: [
@@ -17,11 +22,18 @@ import {HttpClientModule} from '@angular/common/http';
HomePageComponent,
FundingBasketComponent,
CupboardComponent,
- NeedListComponent
+ NeedListComponent,
+ DashboardComponent,
+ LoginComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
+ FormsModule,
+ 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 9c5fce9..c73849f 100644
--- a/ufund-ui/src/app/components/dashboard/dashboard.component.html
+++ b/ufund-ui/src/app/components/dashboard/dashboard.component.html
@@ -1 +1,5 @@
<p>dashboard works!</p>
+<ul>
+ <li><a routerLink="/cupboard">Go to the Cupboard</a></li>
+ <li><a routerLink="/basket">Go to my basket</a></li>
+</ul>
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/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.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 41427ae..2cdb6d0 100644
--- a/ufund-ui/src/app/components/login/login.component.html
+++ b/ufund-ui/src/app/components/login/login.component.html
@@ -1,5 +1,7 @@
+<span *ngIf="next" style="color: red">You must be logged in to view this page</span>
<p>Login:</p>
-<input placeholder="Username" type="text">
-<input placeholder="Password" type="password">
-<button>Login</button>
-<button>Create Account...</button>
+<input placeholder="Username" type="text" #username>
+<input placeholder="Password" type="password" #password>
+<button type="button" (click)="login(username.value, password.value)">Login</button>
+<button type="button" (click)="signup(username.value, password.value)">Create Account</button>
+<span *ngIf="statusText">{{statusText | async}}</span>
diff --git a/ufund-ui/src/app/components/login/login.component.ts b/ufund-ui/src/app/components/login/login.component.ts
index efb8a58..9d806f5 100644
--- a/ufund-ui/src/app/components/login/login.component.ts
+++ b/ufund-ui/src/app/components/login/login.component.ts
@@ -1,4 +1,7 @@
-import { Component } from '@angular/core';
+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',
@@ -6,6 +9,92 @@ import { Component } from '@angular/core';
templateUrl: './login.component.html',
styleUrl: './login.component.css'
})
-export class LoginComponent {
+export class LoginComponent implements OnInit {
+ protected next?: string | null;
+ protected statusText = new BehaviorSubject("")
+
+ constructor(
+ protected usersService: UsersService,
+ protected 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([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/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<boolean> {
- return this.http.post<boolean>(
- this.url, need, this.httpOptions)
+ return this.http.post<boolean>(this.url, need, this.httpOptions)
}
getNeeds(): Observable<Need[]> {
diff --git a/ufund-ui/src/app/services/users.service.ts b/ufund-ui/src/app/services/users.service.ts
index 571c004..c570ccf 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 {BehaviorSubject, firstValueFrom, Observable} from 'rxjs';
import {User} from '../models/User';
@Injectable({
@@ -8,22 +8,35 @@ import {User} from '../models/User';
})
export class UsersService {
- private currentUserID? : number
+ private currentUser : BehaviorSubject<User | null> = new BehaviorSubject<User | null>(null);
+ 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(
private http: HttpClient
) {}
- createUser(data: User): Observable<User> {
- return this.http.post<User>(this.url, data, this.httpOptions)
+ async createUser(username:string, password:string) {
+ await firstValueFrom(this.http.post<User>(this.url, {username: username, password: password}, this.httpOptions))
}
- getUser(id: number): Observable<User> {
+ getUser(id: string): Observable<User> {
return this.http.get<User>(`${this.url}/${id}`, this.httpOptions)
}
@@ -35,7 +48,26 @@ export class UsersService {
return this.http.delete<boolean>(`${this.url}/${id}`, this.httpOptions)
}
- getCurrentUser(): Observable<User> | undefined {
- return this.currentUserID ? this.getUser(this.currentUserID) : undefined
+ getCurrentUserSubject() {
+ return this.currentUser;
+ }
+
+ getCurrentUser() {
+ return this.currentUser.getValue()
+ }
+
+ async login(username: string, password: string) {
+ let res = this.http.post<string>(this.authUrl, {username: username, password: password}, this.httpOptions2);
+ this.apiKey = await firstValueFrom(res);
+ console.log("apikey: "+this.apiKey)
+ let res2 = this.http.get<User>(`${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))
}
}