diff options
Diffstat (limited to '')
24 files changed, 412 insertions, 370 deletions
diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java index d2029ed..bbfd3f6 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 @@ -5,6 +5,7 @@ import java.util.Map;  import java.util.logging.Level;  import java.util.logging.Logger; +import com.ufund.api.ufundapi.service.AuthService;  import org.springframework.http.HttpStatus;  import org.springframework.http.ResponseEntity;  import org.springframework.web.bind.annotation.DeleteMapping; @@ -23,21 +24,21 @@ import com.ufund.api.ufundapi.model.Need;  import com.ufund.api.ufundapi.model.Need.GoalType;  import com.ufund.api.ufundapi.service.CupboardService; -import static java.util.List.of; -  @RestController  @RequestMapping("cupboard")  public class CupboardController {      private static final Logger LOG = Logger.getLogger(CupboardController.class.getName());      private final CupboardService cupboardService; +    private final AuthService authService;      /**       * Create a cupboard controller to receive REST signals       *       * @param cupboardService The Data Access Object       */ -    public CupboardController(CupboardService cupboardService) { +    public CupboardController(CupboardService cupboardService, AuthService authService) {          this.cupboardService = cupboardService; +        this.authService = authService;      }      /** @@ -50,14 +51,15 @@ public class CupboardController {       *         INTERNAL_SERVER_ERROR otherwise       */      @PostMapping("") -    public ResponseEntity<Need> createNeed(@RequestBody Map<String, Object> params) { +    public ResponseEntity<Need> createNeed(@RequestBody Map<String, Object> params, @RequestHeader("jelly-api-key") String key) {          LOG.log(Level.INFO, "POST /cupboard body={0}", params);          String name = (String) params.get("name"); -        double maxGoal = (double) params.get("maxGoal"); +        double maxGoal = ((Number) params.get("maxGoal")).doubleValue();          Need.GoalType goalType = GoalType.valueOf((String) params.get("type"));          try { +            authService.keyHasAccessToCupboard(key);              Need need = cupboardService.createNeed(name, maxGoal, goalType);              return new ResponseEntity<>(need, HttpStatus.OK);          } catch (DuplicateKeyException ex) { @@ -66,6 +68,9 @@ public class CupboardController {          } catch (IllegalArgumentException ex) {              LOG.log(Level.WARNING, ex.getLocalizedMessage());              return new ResponseEntity<>(HttpStatus.BAD_REQUEST); +        } catch (IllegalAccessException ex) { +            LOG.log(Level.WARNING, ex.getLocalizedMessage()); +            return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);          } catch (IOException ex) {              LOG.log(Level.SEVERE, ex.getLocalizedMessage());              return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); @@ -152,9 +157,10 @@ public class CupboardController {       * @return OK response and the need if it was successful, or INTERNAL_SERVER_ERROR if there was an issue       */      @PutMapping("/{id}") -    public ResponseEntity<Need> updateNeed(@RequestBody Need need, @PathVariable int id) { +    public ResponseEntity<Need> updateNeed(@RequestBody Need need, @PathVariable int id, @RequestHeader("jelly-api-key") String key) {          LOG.log(Level.INFO, "PUT /cupboard/{0} body={1}", of(id, need));          try { +            authService.keyHasAccessToCupboard(key);              Need updatedNeed = cupboardService.updateNeed(need, id);              if (updatedNeed != null) {                  return new ResponseEntity<>(need, HttpStatus.OK); @@ -164,6 +170,9 @@ public class CupboardController {          } catch (IllegalArgumentException ex) {              LOG.log(Level.WARNING, ex.getLocalizedMessage());              return new ResponseEntity<>(HttpStatus.BAD_REQUEST); +        } catch (IllegalAccessException ex) { +            LOG.log(Level.WARNING, ex.getLocalizedMessage()); +            return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);          } catch (IOException ex) {              LOG.log(Level.SEVERE, ex.getLocalizedMessage());              return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); @@ -204,19 +213,27 @@ public class CupboardController {       * @return OK if the need was deleted, NOT_FOUND if the need was not found, or INTERNAL_SERVER_ERROR if an error occurred      */      @DeleteMapping("/{id}") -    public ResponseEntity<Need> deleteNeed(@PathVariable int id) { +    public ResponseEntity<Need> deleteNeed(@PathVariable int id, @RequestHeader("jelly-api-key") String key) {          LOG.log(Level.INFO, "DELETE /cupboard/{0}", id);          try { +            authService.keyHasAccessToCupboard(key);              Need need = cupboardService.getNeed(id);              if (cupboardService.deleteNeed(id)) {                  return new ResponseEntity<>(need, HttpStatus.OK);              } else {                  return new ResponseEntity<>(HttpStatus.NOT_FOUND); -            }  +            } +        } catch (IllegalAccessException ex) { +            LOG.log(Level.WARNING, ex.getLocalizedMessage()); +            return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);          } catch (IOException ex) {              LOG.log(Level.SEVERE, ex.getLocalizedMessage());              return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);          }      } +    private Object[] of(Object ...params) { +        return params; +    } +  } 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 c2d9e06..33d2e4f 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 @@ -22,8 +22,6 @@ import com.ufund.api.ufundapi.model.User;  import com.ufund.api.ufundapi.service.AuthService;  import com.ufund.api.ufundapi.service.UserService; -import static java.util.List.of; -  @RestController  @RequestMapping("users")  public class UserController { @@ -79,7 +77,7 @@ public class UserController {          LOG.log(Level.INFO, "GET /user/{0} key={1}", of(username, key));          try { -            authService.authenticate(username, key); +            authService.keyHasAccessToUser(username, key);              User user = userService.getUser(username);              if (user != null) {                  return new ResponseEntity<>(user.withoutPasswordHash(), HttpStatus.OK); @@ -109,7 +107,7 @@ public class UserController {      public ResponseEntity<User> updateUser(@RequestBody User user, @PathVariable String username, @RequestHeader("jelly-api-key") String key) {          LOG.log(Level.INFO,"PUT /users/{0} body={1} key={2}", of(username, user, key));          try { -            authService.authenticate(username, key); +            authService.keyHasAccessToUser(username, key);              user = userService.updateUser(user, username);              if (user != null) {                  return new ResponseEntity<>(user, HttpStatus.OK); @@ -141,7 +139,7 @@ public class UserController {          LOG.log(Level.INFO, "DELETE /users/{0} id={1}", of(username, key));          try { -            authService.authenticate(username, key); +            authService.keyHasAccessToUser(username, key);              if (userService.deleteUser(username)) {                  return new ResponseEntity<>(HttpStatus.OK);              } else { @@ -156,4 +154,8 @@ public class UserController {          }      } +    private Object[] of(Object ...params) { +        return params; +    } +  } 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 786b104..22e86e3 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 @@ -38,7 +38,7 @@ public class 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) { // TODO why is this needed +    public Need(String name, GoalType type, double maxGoal) {          this.name = name;          this.type = type;          this.maxGoal = maxGoal; diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java index 9023b42..24a426b 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java @@ -37,7 +37,7 @@ public class UserAuthFIleDAO implements UserAuthDAO {          UserAuth[] userAuthKeysArray = objectMapper.readValue(new File(filename), UserAuth[].class);          for (UserAuth userAuth : userAuthKeysArray) { -            if (userAuth.getExpiration().compareTo(LocalDateTime.now()) > -1) { // Someone else double check the logic is correct. Checks if auth is valid and adds if so +            if (userAuth.getExpiration().isAfter(LocalDateTime.now())) {                  userAuthMap.put(userAuth.getKey(), userAuth);              }          } 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 0d9b9e4..ec94da8 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,7 +2,6 @@ package com.ufund.api.ufundapi.persistence;  import java.io.File;  import java.io.IOException; -import java.util.ArrayList;  import java.util.HashMap;  import java.util.Map;  import java.util.Objects; @@ -81,15 +80,8 @@ public class UserFileDAO implements UserDAO {      public User updateUser(User user) throws IOException {          synchronized (users) {              if (users.containsKey(user.getUsername())) { -                if (user.getBasket() == null || user.getType() == null) { // TODO clean this up -                    User oldData = users.get(user.getUsername()); -                    User crutch = new User(oldData.getUsername(), 0, new ArrayList<>(), oldData.getType()); -                    crutch.copyPassword(oldData); -                    users.put(user.getUsername(), crutch); -                } else { -                    var old = users.put(user.getUsername(), user); -                    user.copyPassword(Objects.requireNonNull(old)); -                } +                var old = users.put(user.getUsername(), user); +                user.copyPassword(Objects.requireNonNull(old));                  save();                  return user;              } else { diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java index 4e5ebce..cdce80d 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java @@ -25,8 +25,9 @@ public class AuthService {       * @param targetUsername The targetUsername of the user trying to be accessed.       * @param key The api key obtained by the client from logging in.       * @throws IllegalAccessException Thrown if access was denied to the user. +     * @throws IOException Thrown on a file writing issue       */ -    public void authenticate(String targetUsername, String key) throws IllegalAccessException, IOException { +    public void keyHasAccessToUser(String targetUsername, String key) throws IllegalAccessException, IOException {          var userAuth = userAuthDAO.getUserAuth(key);          if (userAuth == null) {              throw new IllegalAccessException("Invalid authentication key"); @@ -39,11 +40,36 @@ public class AuthService {         }      } -    public void authenticate(String key) throws IOException, IllegalAccessException { +    /** +     * Check if the provided key is valid +     * @param key The api key obtained by the client from logging in. +     * @throws IllegalAccessException Thrown if access was denied to the user. +     * @throws IOException Thrown on a file writing issue +     */ +    public void keyIsValid(String key) throws IOException, IllegalAccessException { +        var userAuth = userAuthDAO.getUserAuth(key); +        if (userAuth == null) { +            throw new IllegalAccessException("Invalid authentication key"); +        } +    } + +    /** +     * Check if the provided key has access to edit the cupboard +     * @param key The api key obtained by the client from logging in. +     * @throws IllegalAccessException Thrown if access was denied to the user. +     * @throws IOException Thrown on a file writing issue +     */ +    public void keyHasAccessToCupboard(String key) throws IOException, IllegalAccessException {          var userAuth = userAuthDAO.getUserAuth(key);          if (userAuth == null) {              throw new IllegalAccessException("Invalid authentication key");          } + +        var username = userAuth.getUsername(); +        var userType = userService.getUser(username).getType(); +        if (userType != User.UserType.MANAGER) { +            throw new IllegalAccessException("Provided key does not grant access to perform the requested operation"); +        }      }      /** diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java index 91e3ba5..aaa8cb8 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java @@ -111,7 +111,7 @@ public class CupboardService {          if (checkoutAmount <= 0) {              throw new IllegalArgumentException("Amount must be greater than 0");          } -        authService.authenticate(key); +        authService.keyIsValid(key);          Need need = cupboardDAO.getNeed(id);          need.incrementCurrent(checkoutAmount);      } @@ -124,6 +124,7 @@ public class CupboardService {       * @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/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/CupboardControllerTest.java index 6ef6710..89697bf 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 @@ -7,10 +7,11 @@ import static java.util.Map.entry;  import static org.junit.jupiter.api.Assertions.assertArrayEquals;  import static org.junit.jupiter.api.Assertions.assertEquals;  import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.*; + +import com.ufund.api.ufundapi.service.AuthService;  import org.junit.jupiter.api.BeforeEach;  import org.junit.jupiter.api.Test; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when;  import org.springframework.http.HttpStatus;  import com.ufund.api.ufundapi.DuplicateKeyException; @@ -21,11 +22,17 @@ import com.ufund.api.ufundapi.service.CupboardService;  public class CupboardControllerTest {      private CupboardController cupboardController;      private CupboardService mockCupboardService; +    private final String key = "dummyKey";      @BeforeEach      public void setupCupboardDAO() { +        AuthService mockAuthService = mock(AuthService.class);          mockCupboardService = mock(CupboardService.class); -        cupboardController = new CupboardController(mockCupboardService); +        cupboardController = new CupboardController(mockCupboardService, mockAuthService); + +        try { +            doThrow().when(mockAuthService).keyHasAccessToCupboard(key); +        } catch (Exception ignored) {}      }      @Test @@ -43,7 +50,7 @@ public class CupboardControllerTest {                  entry("type", "MONETARY")          ); -        var res = cupboardController.createNeed(needMap); +        var res = cupboardController.createNeed(needMap, key);          assertEquals(HttpStatus.OK, res.getStatusCode());          assertEquals(need, res.getBody()); @@ -58,7 +65,7 @@ public class CupboardControllerTest {                  entry("maxGoal", -100.0),                  entry("type", "MONETARY")); -        var res = cupboardController.createNeed(needMap); +        var res = cupboardController.createNeed(needMap, key);          assertEquals(HttpStatus.BAD_REQUEST, res.getStatusCode());      } @@ -72,7 +79,7 @@ public class CupboardControllerTest {                  entry("maxGoal", 100.0),                  entry("type", "MONETARY")); -        var res = cupboardController.createNeed(needMap); +        var res = cupboardController.createNeed(needMap, key);          assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, res.getStatusCode());      } @@ -174,7 +181,7 @@ public class CupboardControllerTest {          var need = new Need("Name", 1, 100, Need.GoalType.MONETARY);          when(mockCupboardService.updateNeed(need, 1)).thenReturn(need); -        var res = cupboardController.updateNeed(need, 1); +        var res = cupboardController.updateNeed(need, 1, key);          assertEquals(HttpStatus.OK, res.getStatusCode());          assertEquals(need, res.getBody()); @@ -185,7 +192,7 @@ public class CupboardControllerTest {          var need = new Need("Name", 1, 100, Need.GoalType.MONETARY);          when(mockCupboardService.updateNeed(need, 1)).thenThrow(new IOException()); -        var res = cupboardController.updateNeed(need, 1); +        var res = cupboardController.updateNeed(need, 1, key);          assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, res.getStatusCode());      } @@ -196,7 +203,7 @@ public class CupboardControllerTest {          when(mockCupboardService.getNeed(1)).thenReturn(need);          when(mockCupboardService.deleteNeed(1)).thenReturn(true); -        var res = cupboardController.deleteNeed(1); +        var res = cupboardController.deleteNeed(1, key);          assertEquals(HttpStatus.OK, res.getStatusCode());      } @@ -206,7 +213,7 @@ public class CupboardControllerTest {          when(mockCupboardService.getNeed(1)).thenReturn(null);          when(mockCupboardService.deleteNeed(1)).thenReturn(false); -        var res = cupboardController.deleteNeed(1); +        var res = cupboardController.deleteNeed(1, key);          assertEquals(HttpStatus.NOT_FOUND, res.getStatusCode());      } @@ -217,7 +224,7 @@ public class CupboardControllerTest {          when(mockCupboardService.getNeed(1)).thenReturn(need);          when(mockCupboardService.deleteNeed(1)).thenThrow(new IOException()); -        var res = cupboardController.deleteNeed(1); +        var res = cupboardController.deleteNeed(1, key);          assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, res.getStatusCode());      } 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 cc7df40..06fb6cd 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 @@ -82,7 +82,7 @@ public class UserControllerTest {          String key = UserAuth.generate(username).getKey();          // When getUser is called on the Mock User service, throw an IOException          // doThrow(new IllegalAccessException()).when(mockUserService).getUser(username); -        doThrow(new IllegalAccessException()).when(mockAuthService).authenticate(username, key); +        doThrow(new IllegalAccessException()).when(mockAuthService).keyHasAccessToUser(username, key);          // Invoke          ResponseEntity<User> response = userController.getUser(username, key); @@ -237,7 +237,7 @@ public class UserControllerTest {          String key = UserAuth.generate(username).getKey();          // When updateUser is called on the Mock User service, throw a Invalid Parameter exception          // exception -        doThrow(new IllegalAccessException()).when(mockAuthService).authenticate(username, key); +        doThrow(new IllegalAccessException()).when(mockAuthService).keyHasAccessToUser(username, key);          // Invoke @@ -298,7 +298,7 @@ public class UserControllerTest {          String username = "Test";          String key = UserAuth.generate(username).getKey();          // When deleteUser is called on the Mock User service, throw an IOException -        doThrow(new IllegalAccessException()).when(mockAuthService).authenticate(username, key); +        doThrow(new IllegalAccessException()).when(mockAuthService).keyHasAccessToUser(username, key);          // Invoke          ResponseEntity<Boolean> response = userController.deleteUser(username, key); diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserAuthFileDAOTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserAuthFileDAOTest.java index 5e92deb..a4842c5 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserAuthFileDAOTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserAuthFileDAOTest.java @@ -43,7 +43,7 @@ public class UserAuthFileDAOTest {          String key = "123";          UserAuth auth = userAuthFIleDAO.getUserAuth(key); -        assertEquals(auth, userAuths[0]); +        assertEquals(userAuths[0], auth);      }      @Test diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/service/AuthServiceTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/service/AuthServiceTest.java index d3085e5..4f58b12 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/service/AuthServiceTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/service/AuthServiceTest.java @@ -40,34 +40,34 @@ public class AuthServiceTest {      }      @Test -    public void testAuthenticate() throws IOException { +    public void testKeyIsValid() throws IOException {          // Mock          when(mockAuthDAO.getUserAuth(key)).thenReturn(new UserAuth(key, username, null));          when(mockUserService.getUser(username)).thenReturn(user);          // Analyze -        assertDoesNotThrow(() -> authService.authenticate(username, key)); +        assertDoesNotThrow(() -> authService.keyHasAccessToUser(username, key));      }      @Test -    public void testAuthenticateMismatchName() throws IOException { +    public void testKeyIsValidMismatchName() throws IOException {          // Mock          when(mockAuthDAO.getUserAuth(key)).thenReturn(new UserAuth(key, "EvilFish", null));          when(mockUserService.getUser("EvilFish")).thenReturn(user);          // Analyze -        assertThrows(IllegalAccessException.class, () -> authService.authenticate(username, key)); +        assertThrows(IllegalAccessException.class, () -> authService.keyHasAccessToUser(username, key));      }      @Test -    public void testAuthenticateMissingUserAuth() throws IOException { +    public void testKeyIsValidMissingUserAuth() throws IOException {          // Mock          when(mockAuthDAO.getUserAuth(key)).thenReturn(null);          // Analyze -        assertThrows(IllegalAccessException.class, () -> authService.authenticate(username, key)); +        assertThrows(IllegalAccessException.class, () -> authService.keyHasAccessToUser(username, key));      } diff --git a/ufund-ui/src/app/app.component.ts b/ufund-ui/src/app/app.component.ts index 7dc8ffb..86717c4 100644 --- a/ufund-ui/src/app/app.component.ts +++ b/ufund-ui/src/app/app.component.ts @@ -1,7 +1,7 @@  import {Component, OnInit, Inject} from '@angular/core'; -import {UsersService} from './services/users.service';  import {BehaviorSubject} from 'rxjs';  import { DOCUMENT } from '@angular/common'; +import {AuthService} from './services/auth.service';  @Component({    selector: 'app-root', @@ -14,16 +14,16 @@ export class AppComponent implements OnInit {      currentUser$: BehaviorSubject<string> = new BehaviorSubject<string>("Logged out.");      constructor( -        private userService: UsersService, +        private authService: AuthService,          @Inject(DOCUMENT) private document: Document      ) {}      reloadPage() {          this.document.defaultView?.location.reload(); -      } +    }      ngOnInit() { -        this.userService.getCurrentUserSubject().subscribe(r => { +        this.authService.getCurrentUserSubject().subscribe(r => {              this.currentUser$?.next(r                  ? "Logged in as " + r.username                  : "Logged out." diff --git a/ufund-ui/src/app/components/cupboard/cupboard.component.ts b/ufund-ui/src/app/components/cupboard/cupboard.component.ts index 24b3e2d..a812baf 100644 --- a/ufund-ui/src/app/components/cupboard/cupboard.component.ts +++ b/ufund-ui/src/app/components/cupboard/cupboard.component.ts @@ -1,10 +1,10 @@  import { Component, OnInit, ViewChild } from '@angular/core';  import { CupboardService } from '../../services/cupboard.service'; -import { UsersService } from '../../services/users.service';  import { Need, GoalType } from '../../models/Need';  import { userType } from '../../models/User';  import { BehaviorSubject, catchError, of } from 'rxjs';  import { NeedListComponent } from '../need-list/need-list.component'; +import {AuthService} from '../../services/auth.service';  @Component({      selector: 'app-cupboard', @@ -20,7 +20,10 @@ export class CupboardComponent implements OnInit {      needs: any;      @ViewChild("needList") needList?: NeedListComponent -    constructor(private cupboardService: CupboardService, private usersService: UsersService) { } +    constructor( +        private cupboardService: CupboardService, +        private authService: AuthService +    ) {}      ngOnInit(): void {          this.cupboardService.getNeeds().subscribe(n => this.needs = n); @@ -88,7 +91,7 @@ export class CupboardComponent implements OnInit {      }      isManager() { -        const type = this.usersService.getCurrentUser()?.type; +        const type = this.authService.getCurrentUser()?.type;          return type === ("MANAGER" as unknown as userType);      } @@ -105,7 +108,7 @@ export class CupboardComponent implements OnInit {          console.log("need:", need);          console.log(need.id, need, "need updated");          this.cupboardService.updateNeed(need.id, need) -            .pipe(catchError((ex, r) => { +            .pipe(catchError((ex, _) => {                  if (ex.status == 500) {                      this.statusText.next("Fields cannot be blank");                  } else if (ex.status == 400) { @@ -140,7 +143,7 @@ export class CupboardComponent implements OnInit {          console.log("need:", need);          console.log("form submitted. creating need: ", need);          this.cupboardService.createNeed(need) -            .pipe(catchError((ex, r) => { +            .pipe(catchError((ex, _) => {                  if (ex.status == 500) {                      this.statusText.next("Fields cannot be blank");                  } else if (ex.status == 400) { @@ -167,48 +170,3 @@ export class CupboardComponent implements OnInit {      }  } - -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', -    422: 'Unprocessable Entity', -    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/components/dashboard/dashboard.component.ts b/ufund-ui/src/app/components/dashboard/dashboard.component.ts index b9faefa..a0ad566 100644 --- a/ufund-ui/src/app/components/dashboard/dashboard.component.ts +++ b/ufund-ui/src/app/components/dashboard/dashboard.component.ts @@ -1,21 +1,21 @@ -import { Component } from '@angular/core'; -import { UsersService } from '../../services/users.service'; -import { userType } from '../../models/User'; +import {Component} from '@angular/core'; +import {userType} from '../../models/User'; +import {AuthService} from '../../services/auth.service';  @Component({ -  selector: 'app-dashboard', -  standalone: false, -  templateUrl: './dashboard.component.html', -  styleUrl: './dashboard.component.css' +    selector: 'app-dashboard', +    standalone: false, +    templateUrl: './dashboard.component.html', +    styleUrl: './dashboard.component.css'  })  export class DashboardComponent {      constructor( -      protected usersService: UsersService, +        protected authService: AuthService,      ) {}      isManager() { -        const type = this.usersService.getCurrentUser()?.type; +        const type = this.authService.getCurrentUser()?.type;          return type === ("MANAGER" as unknown as userType); -      } +    }  } 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 e654711..faa7e0b 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,11 +1,9 @@  import {Component, Input, OnInit, ViewChild} from '@angular/core'; -import {User} from '../../models/User'; -import { UsersService } from '../../services/users.service'; -import { Need } from '../../models/Need'; -import { NeedListComponent } from '../need-list/need-list.component'; -import { Router } from '@angular/router'; -import { CupboardService } from '../../services/cupboard.service'; -import { BehaviorSubject, catchError, firstValueFrom, Observable } from 'rxjs'; +import {UsersService} from '../../services/users.service'; +import {Router} from '@angular/router'; +import {CupboardService} from '../../services/cupboard.service'; +import {catchError, firstValueFrom, Observable} from 'rxjs'; +import {AuthService} from '../../services/auth.service';  @Component({      selector: 'app-funding-basket', @@ -14,67 +12,67 @@ import { BehaviorSubject, catchError, firstValueFrom, Observable } from 'rxjs';      styleUrl: './funding-basket.component.css'  })  export class FundingBasketComponent implements OnInit { -  statusText: any; +    statusText: any; -  constructor( -    private router: Router, -    protected cupboardService: CupboardService,  -    protected usersService: UsersService -  ) {} +    constructor( +        private router: Router, +        protected cupboardService: CupboardService, +        protected usersService: UsersService, +        private authService: AuthService +    ) {} -  @ViewChild("contribution") contribution?: Input; -  @Input() isValid: boolean = true; +    @ViewChild("contribution") contribution?: Input; +    @Input() isValid: boolean = true; -  // this is for login rerouting -  ngOnInit(): void { -    if (!this.usersService.getCurrentUser()) { -      this.router.navigate(['/login'], {queryParams: {redir: this.router.url}}); -      return; -    } - -    this.usersService.refreshBasket(); -    // this.usersService.removeNeed(); <- call this to remove -  } +    // this is for login rerouting +    ngOnInit(): void { +        if (!this.authService.getCurrentUser()) { +            this.router.navigate(['/login'], {queryParams: {redir: this.router.url}}); +            return; +        } -  async checkout() { -    this.isValid = true; -    for (let c of document.getElementById("funding-basket")?.querySelectorAll('.contribution')!) { -      let contribution = c as HTMLInputElement; -      contribution.setAttribute("style",""); -      if ( contribution.value == '' || contribution.valueAsNumber <= 0) { -        this.isValid = false; -        contribution.setAttribute("style","color: #ff0000"); -      } +        this.usersService.refreshBasket(); +        // this.usersService.removeNeed(); <- call this to remove      } -    if (this.isValid) { -      for (let c of document.getElementById("funding-basket")?.querySelectorAll('.contribution')!) { -        let contribution = c as HTMLInputElement; -        let need = await firstValueFrom(this.cupboardService.getNeed(+contribution.id)); -        need.current +=+ contribution.value; -        this.usersService.removeNeed(+need.id); -        this.cupboardService.updateNeed(need.id, need) -            .pipe(catchError((ex, r) => { -                if (ex.status == 500) { -                    this.statusText.next('Fields cannot be blank'); -                } else if (ex.status == 400) { -                    this.statusText.next('Goal must be greater than 0'); -                } else { -                    this.statusText.next('Error on creating need'); -                } -                return new Observable<string>(); -            })) -            .subscribe((result) => { -                if (result) { -                    console.log('need updated successfully'); -                    //this.needList?.refresh() -                } else { -                    console.log('need update failed'); -                } -            }); -      } -    } -  } +    async checkout() { +        this.isValid = true; +        for (let c of document.getElementById("funding-basket")?.querySelectorAll('.contribution')!) { +            let contribution = c as HTMLInputElement; +            contribution.setAttribute("style", ""); +            if (contribution.value == '' || contribution.valueAsNumber <= 0) { +                this.isValid = false; +                contribution.setAttribute("style", "color: #ff0000"); +            } +        } +        if (this.isValid) { +            for (let c of document.getElementById("funding-basket")?.querySelectorAll('.contribution')!) { +                let contribution = c as HTMLInputElement; +                let need = await firstValueFrom(this.cupboardService.getNeed(+contribution.id)); +                need.current += +contribution.value; +                this.usersService.removeNeed(+need.id); +                this.cupboardService.checkoutNeed(need.id, +contribution.value) +                    .pipe(catchError((ex, _) => { +                        if (ex.status == 500) { +                            this.statusText.next('Fields cannot be blank'); +                        } else if (ex.status == 400) { +                            this.statusText.next('Goal must be greater than 0'); +                        } else { +                            this.statusText.next('Error on creating need'); +                        } +                        return new Observable<string>(); +                    })) +                    .subscribe((result) => { +                        if (result) { +                            console.log('need updated successfully'); +                            //this.needList?.refresh() +                        } else { +                            console.log('need update failed'); +                        } +                    }); +            } +        } +    }  } diff --git a/ufund-ui/src/app/components/home-page/home-page.component.ts b/ufund-ui/src/app/components/home-page/home-page.component.ts index 5b2277c..95e8962 100644 --- a/ufund-ui/src/app/components/home-page/home-page.component.ts +++ b/ufund-ui/src/app/components/home-page/home-page.component.ts @@ -1,10 +1,10 @@ -import { Component } from '@angular/core'; +import {Component} from '@angular/core';  @Component({ -  selector: 'app-home-page', -  standalone: false, -  templateUrl: './home-page.component.html', -  styleUrl: './home-page.component.css' +    selector: 'app-home-page', +    standalone: false, +    templateUrl: './home-page.component.html', +    styleUrl: './home-page.component.css'  })  export class HomePageComponent { diff --git a/ufund-ui/src/app/components/login/login.component.ts b/ufund-ui/src/app/components/login/login.component.ts index 9d806f5..f6a2996 100644 --- a/ufund-ui/src/app/components/login/login.component.ts +++ b/ufund-ui/src/app/components/login/login.component.ts @@ -2,12 +2,13 @@ import {Component, OnInit} from '@angular/core'  import {UsersService} from '../../services/users.service';  import {ActivatedRoute, Router} from '@angular/router';  import {BehaviorSubject} from 'rxjs'; +import {AuthService} from '../../services/auth.service';  @Component({ -  selector: 'app-login', -  standalone: false, -  templateUrl: './login.component.html', -  styleUrl: './login.component.css' +    selector: 'app-login', +    standalone: false, +    templateUrl: './login.component.html', +    styleUrl: './login.component.css'  })  export class LoginComponent implements OnInit { @@ -17,7 +18,8 @@ export class LoginComponent implements OnInit {      constructor(          protected usersService: UsersService,          protected router: Router, -        private route: ActivatedRoute +        private route: ActivatedRoute, +        private authService: AuthService      ) {}      ngOnInit() { @@ -31,7 +33,7 @@ export class LoginComponent implements OnInit {              return;          } -        this.usersService.login(username, password).then(() => { +        this.authService.login(username, password).then(() => {              this.router.navigate([next]);          }).catch(ex => {              this.statusText.next("Unable to login: " + friendlyHttpStatus[ex.status]) diff --git a/ufund-ui/src/app/components/need-list/need-list.component.ts b/ufund-ui/src/app/components/need-list/need-list.component.ts index 25f05d6..3a89a20 100644 --- a/ufund-ui/src/app/components/need-list/need-list.component.ts +++ b/ufund-ui/src/app/components/need-list/need-list.component.ts @@ -1,132 +1,138 @@ -import { Component } from '@angular/core'; +import {Component} from '@angular/core';  import {Need} from '../../models/Need';  import {CupboardService} from '../../services/cupboard.service'; -import { UsersService } from '../../services/users.service'; -import { userType } from '../../models/User'; +import {UsersService} from '../../services/users.service'; +import {userType} from '../../models/User'; +import {catchError, of} from 'rxjs'; +import {AuthService} from '../../services/auth.service'; +  @Component({ -  selector: 'app-need-list', -  standalone: false, -  templateUrl: './need-list.component.html', -  styleUrl: './need-list.component.css' +    selector: 'app-need-list', +    standalone: false, +    templateUrl: './need-list.component.html', +    styleUrl: './need-list.component.css'  })  export class NeedListComponent { -  needs: Need[] = []; -  searchResults: Need[] = []; +    needs: Need[] = []; +    searchResults: Need[] = []; -  constructor( -    private cupboardService: CupboardService, -    private usersService: UsersService -  ) {} +    constructor( +        private cupboardService: CupboardService, +        private usersService: UsersService, +        private authService: AuthService +    ) {}      refresh() {          this.cupboardService.getNeeds().subscribe(n => this.needs = n)      } -  ngOnInit(): void { -    this.refresh() -    -     this.close(); -  } +    ngOnInit(): void { +        this.refresh() -  private showElement(element: any) { -    if (element){ -      element.style.visibility = 'visible'; -      element.style.position = 'relative'; +        this.close();      } -  } -  private hideElement(element: any) { -    if (element){ -      element.style.visibility = 'hidden'; -      element.style.position = 'absolute'; +    private showElement(element: any) { +        if (element) { +            element.style.visibility = 'visible'; +            element.style.position = 'relative'; +        }      } -  } -  private updateSearchStatus(text: string) { -    let element = document.getElementById('search-status'); -    if (element) { -      element.innerHTML = text; +    private hideElement(element: any) { +        if (element) { +            element.style.visibility = 'hidden'; +            element.style.position = 'absolute'; +        }      } -  } -  open() { -    this.hideElement(document.getElementById('search-button')); -    this.showElement(document.getElementById('search-form')); -  } +    private updateSearchStatus(text: string) { +        let element = document.getElementById('search-status'); +        if (element) { +            element.innerHTML = text; +        } +    } -  close() { -    this.hideElement(document.getElementById('search-form')); -    this.showElement(document.getElementById('search-button')); -    this.hideElement(document.getElementById('search-status')); -  } +    open() { +        this.hideElement(document.getElementById('search-button')); +        this.showElement(document.getElementById('search-form')); +    } -  private searchDelay: any; +    close() { +        this.hideElement(document.getElementById('search-form')); +        this.showElement(document.getElementById('search-button')); +        this.hideElement(document.getElementById('search-status')); +    } -  async search(form: any) { -    //wait .25 seconds before searching but cancel if another search is made during the wait to prevent too many api calls +    private searchDelay: any; -    //remove previous search if it exists -    if (this.searchDelay) { -      clearTimeout(this.searchDelay); -    } +    async search(form: any) { +        //wait .25 seconds before searching but cancel if another search is made during the wait to prevent too many api calls -    this.searchDelay = setTimeout(() => { -      const currentSearchValue = form.search; //latest value of the search -      this.cupboardService.searchNeeds(currentSearchValue).subscribe((n) => { -        this.searchResults = n; -        console.log(currentSearchValue, this.searchResults); -        this.showElement(document.getElementById('search-results')); -        this.showElement(document.getElementById('search-status')); -        if (this.searchResults.length === this.needs.length) { -          this.updateSearchStatus("Please refine your search"); -          this.searchResults = []; -        } else if (this.searchResults.length === 0) { -          this.updateSearchStatus("No results found"); -        } else { -          this.updateSearchStatus("Search results:"); +        //remove previous search if it exists +        if (this.searchDelay) { +            clearTimeout(this.searchDelay);          } -      }); -    }, 250); -  } - -  delete(id : number) { -    this.cupboardService.deleteNeed(id).subscribe(() => { -      this.needs = this.needs.filter(n => n.id !== id) -    }) -  } - -  isManager() { -    const type = this.usersService.getCurrentUser()?.type; -    return type === ("MANAGER" as unknown as userType); -  } - -  isHelper() { -    const type = this.usersService.getCurrentUser()?.type; -    return type === ("HELPER" as unknown as userType); -  } - -  add(need: Need) { -    const currentUser = this.usersService.getCurrentUser(); -    //console.log("get current user in angular:", currentUser) -    if (currentUser) { -      if (!currentUser.basket.includes(need.id)) { -        currentUser.basket.push(need.id); -        this.usersService.updateUser(currentUser).subscribe(() => { -          this.usersService.refreshBasket(); -          error: (err: any) => { -            console.error(err); -          } -        }); -      } else { -        window.alert("This need is already in your basket!") -      } -       +        this.searchDelay = setTimeout(() => { +            const currentSearchValue = form.search; //latest value of the search +            this.cupboardService.searchNeeds(currentSearchValue).subscribe((n) => { +                this.searchResults = n; +                console.log(currentSearchValue, this.searchResults); +                this.showElement(document.getElementById('search-results')); +                this.showElement(document.getElementById('search-status')); +                if (this.searchResults.length === this.needs.length) { +                    this.updateSearchStatus("Please refine your search"); +                    this.searchResults = []; +                } else if (this.searchResults.length === 0) { +                    this.updateSearchStatus("No results found"); +                } else { +                    this.updateSearchStatus("Search results:"); +                } +            }); +        }, 250); +    } + +    delete(id: number) { +        this.cupboardService.deleteNeed(id).subscribe(() => { +            this.needs = this.needs.filter(n => n.id !== id) +        })      } -  } +    isManager() { +        const type = this.authService.getCurrentUser()?.type; +        return type === ("MANAGER" as unknown as userType); +    } + +    isHelper() { +        const type = this.authService.getCurrentUser()?.type; +        return type === ("HELPER" as unknown as userType); +    } + +    add(need: Need) { +        const currentUser = this.authService.getCurrentUser(); +        //console.log("get current user in angular:", currentUser) +        if (currentUser) { +            if (!currentUser.basket.includes(need.id)) { +                currentUser.basket.push(need.id); +                this.usersService.updateUser(currentUser) +                    .pipe(catchError((err: any, _) => { +                        console.error(err); +                        return of(); +                    })) +                    .subscribe(() => { +                        this.usersService.refreshBasket(); +                    }); +            } else { +                window.alert("This need is already in your basket!") +            } + -  back() { -    this.searchResults = []; -  } +        } + +    } + +    back() { +        this.searchResults = []; +    }  } diff --git a/ufund-ui/src/app/components/need-page/need-page.component.ts b/ufund-ui/src/app/components/need-page/need-page.component.ts index 597d0e0..e38554c 100644 --- a/ufund-ui/src/app/components/need-page/need-page.component.ts +++ b/ufund-ui/src/app/components/need-page/need-page.component.ts @@ -2,30 +2,30 @@ import {Component, Input} from '@angular/core';  import {GoalType, Need} from '../../models/Need';  import {ActivatedRoute} from "@angular/router";  import {CupboardService} from "../../services/cupboard.service"; -import { NgFor } from '@angular/common'; +import {NgFor} from '@angular/common';  @Component({ -  selector: 'app-need-page', -  standalone: false, -  templateUrl: './need-page.component.html', -  styleUrl: './need-page.component.css' +    selector: 'app-need-page', +    standalone: false, +    templateUrl: './need-page.component.html', +    styleUrl: './need-page.component.css'  })  export class NeedPageComponent { -  constructor( -     private route: ActivatedRoute, -     private cupboardService: CupboardService, -  ) {} +    constructor( +        private route: ActivatedRoute, +        private cupboardService: CupboardService, +    ) {} -  public GoalType = GoalType; +    public GoalType = GoalType; -  @Input() need?: Need; +    @Input() need?: Need; -  ngOnInit(): void { -    const id = Number(this.route.snapshot.paramMap.get('id')); -    this.cupboardService.getNeed(id).subscribe(n => this.need = n); -  } +    ngOnInit(): void { +        const id = Number(this.route.snapshot.paramMap.get('id')); +        this.cupboardService.getNeed(id).subscribe(n => this.need = n); +    } -  back() { -    window.history.back(); -  } -}
\ No newline at end of file +    back() { +        window.history.back(); +    } +} diff --git a/ufund-ui/src/app/models/Need.ts b/ufund-ui/src/app/models/Need.ts index 9e97fd4..5cd4e39 100644 --- a/ufund-ui/src/app/models/Need.ts +++ b/ufund-ui/src/app/models/Need.ts @@ -1,13 +1,13 @@  export interface Need { -  name: string, -  id: number, -  filterAttributes: string[], -  type: GoalType; -  maxGoal: number; -  current: number; +    name: string, +    id: number, +    filterAttributes: string[], +    type: GoalType; +    maxGoal: number; +    current: number;  }  export enum GoalType { -  MONETARY, -  PHYSICAL +    MONETARY, +    PHYSICAL  } diff --git a/ufund-ui/src/app/models/User.ts b/ufund-ui/src/app/models/User.ts index f4396f6..e6848fa 100644 --- a/ufund-ui/src/app/models/User.ts +++ b/ufund-ui/src/app/models/User.ts @@ -1,5 +1,3 @@ -import {Need} from './Need'; -  export enum userType {      HELPER,      MANAGER diff --git a/ufund-ui/src/app/services/auth.service.ts b/ufund-ui/src/app/services/auth.service.ts new file mode 100644 index 0000000..6bc7145 --- /dev/null +++ b/ufund-ui/src/app/services/auth.service.ts @@ -0,0 +1,57 @@ +import {Injectable} from '@angular/core'; +import {BehaviorSubject, firstValueFrom} from 'rxjs'; +import {User} from '../models/User'; +import {HttpClient, HttpHeaders} from '@angular/common/http'; + +@Injectable({ +    providedIn: 'root' +}) +export class AuthService { + +    private authUrl = "http://localhost:8080/auth" +    private userUrl = "http://localhost:8080/users" + +    private currentUser : BehaviorSubject<User | null> = new BehaviorSubject<User | null>(null); +    private apiKey: string = ""; + +    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 +    ) {} + +    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.userUrl}/${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)) +    } + +    getCurrentUserSubject() { +        return this.currentUser; +    } + +    getCurrentUser() { +        return this.currentUser.getValue() +    } + +    getApiKey() { +        return this.apiKey; +    } + +} diff --git a/ufund-ui/src/app/services/cupboard.service.ts b/ufund-ui/src/app/services/cupboard.service.ts index 9e14106..9232c0c 100644 --- a/ufund-ui/src/app/services/cupboard.service.ts +++ b/ufund-ui/src/app/services/cupboard.service.ts @@ -2,6 +2,7 @@ import {Injectable} from '@angular/core';  import {HttpClient, HttpHeaders} from '@angular/common/http';  import {Need} from '../models/Need';  import {Observable} from 'rxjs'; +import {AuthService} from './auth.service';  @Injectable({      providedIn: 'root' @@ -9,35 +10,44 @@ import {Observable} from 'rxjs';  export class CupboardService {      private url = "http://localhost:8080/cupboard" -    private httpOptions = { -        headers: new HttpHeaders({'Content-Type': 'application/json'}) -    }; + +    httpOptions = () => ({ +        headers: new HttpHeaders({ +            'Content-Type': 'application/json', +            "jelly-api-key": this.authService.getApiKey() +        }) +    });      constructor( -        private http: HttpClient +        private http: HttpClient, +        private authService: AuthService      ) {}      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[]> { -        return this.http.get<Need[]>(this.url, this.httpOptions) +        return this.http.get<Need[]>(this.url, this.httpOptions())      }      searchNeeds(name: String): Observable<Need[]> { -        return this.http.get<Need[]>(`${this.url}/?name=${name}`, this.httpOptions) +        return this.http.get<Need[]>(`${this.url}/?name=${name}`, this.httpOptions())      }      getNeed(id: number): Observable<Need> { -        return this.http.get<Need>(`${this.url}/${id}`, this.httpOptions) +        return this.http.get<Need>(`${this.url}/${id}`, this.httpOptions())      }      updateNeed(id: number, data: Need): Observable<boolean> { -        return this.http.put<boolean>(`${this.url}/${id}`, data, this.httpOptions) +        return this.http.put<boolean>(`${this.url}/${id}`, data, this.httpOptions())      }      deleteNeed(id: number): Observable<boolean> { -        return this.http.delete<boolean>(`${this.url}/${id}`, this.httpOptions) +        return this.http.delete<boolean>(`${this.url}/${id}`, this.httpOptions()) +    } + +    checkoutNeed(id: number, quantity: number) { +        return this.http.put(`${this.url}/checkout`, {needID: id, amount: quantity}, this.httpOptions())      }  } diff --git a/ufund-ui/src/app/services/users.service.ts b/ufund-ui/src/app/services/users.service.ts index 6671440..4080ebf 100644 --- a/ufund-ui/src/app/services/users.service.ts +++ b/ufund-ui/src/app/services/users.service.ts @@ -4,36 +4,27 @@ import {BehaviorSubject, catchError, firstValueFrom, Observable, of} from 'rxjs'  import {User} from '../models/User';  import { Need } from '../models/Need';  import { CupboardService } from './cupboard.service'; +import {AuthService} from './auth.service';  @Injectable({    providedIn: 'root'  })  export class UsersService { -    private currentUser : BehaviorSubject<User | null> = new BehaviorSubject<User | null>(null); -    private apiKey: string = "";      private basket = new BehaviorSubject<Need[]>([]); -      private url = "http://localhost:8080/users" -    private authUrl = "http://localhost:8080/auth" -    private httpOptions = () => ({ + +    httpOptions = () => ({          headers: new HttpHeaders({              'Content-Type': 'application/json', -            "jelly-api-key": this.apiKey +            "jelly-api-key": this.authService.getApiKey()          })      }); -    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,          private cupboardService: CupboardService, +        private authService: AuthService      ) {}      async createUser(username:string, password:string) { @@ -46,38 +37,15 @@ export class UsersService {      updateUser(user: User): Observable<User> {          console.log(`${this.url}/${user.username}`, user, this.httpOptions) -        return this.http.put<User>(`${this.url}/${user.username}`, user, this.httpOptions2()) // This line is causing issues as the key is not properly being passed +        return this.http.put<User>(`${this.url}/${user.username}`, user, this.httpOptions())      }      deleteUser(id: number): Observable<boolean> {          return this.http.delete<boolean>(`${this.url}/${id}`, this.httpOptions())      } -    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)) -    } -      refreshBasket() { -        let promiseArr = this.getCurrentUser()!.basket.map(async needID => { +        let promiseArr = this.authService.getCurrentUser()!.basket.map(async needID => {              return await firstValueFrom(this.cupboardService.getNeed(needID));          })          Promise.all(promiseArr).then(r => this.basket.next(r)); @@ -86,10 +54,10 @@ export class UsersService {      removeNeed(id: number) {          let newArr = this.basket.getValue().filter(v => v.id != id);          this.basket.next(newArr); -        this.getCurrentUser()!.basket = newArr.map(need => need.id); -        this.updateUser(this.getCurrentUser()!) +        this.authService.getCurrentUser()!.basket = newArr.map(need => need.id); +        this.updateUser(this.authService.getCurrentUser()!)              .pipe( -                catchError((err: any, ob) => { +                catchError((err: any, _) => {                      console.error(err);                      return of();                  })  | 
