Merge branch '69-7-fixes-to-guest-user-story' into 'dev'

Resolve "7: fixes to guest user story"

Closes #69

See merge request sa4-2020/the-sanmarinoes/backend!112
This commit is contained in:
Claudio Maggioni 2020-05-04 17:15:29 +02:00
commit 38c703e521
4 changed files with 83 additions and 40 deletions

View file

@ -6,20 +6,13 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SceneSaveRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.SceneService; import ch.usi.inf.sa4.sanmarinoes.smarthut.service.SceneService;
import ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils;
import java.security.Principal; import java.security.Principal;
import java.util.List; import java.util.List;
import javax.validation.Valid; import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
@EnableAutoConfiguration @EnableAutoConfiguration
@ -27,21 +20,20 @@ import org.springframework.web.bind.annotation.RestController;
public class SceneController { public class SceneController {
@Autowired private SceneRepository sceneRepository; @Autowired private SceneRepository sceneRepository;
@Autowired private UserRepository userRepository;
@Autowired private SceneService sceneService; @Autowired private SceneService sceneService;
@Autowired private UserRepository userService; @Autowired private StateRepository<State<?>> stateRepository;
@Autowired private StateRepository<State<?>> stateService;
@GetMapping @GetMapping
public List<Scene> findAll(Principal principal) { public List<Scene> findAll(
return toList(sceneRepository.findByUsername(principal.getName())); Principal principal, @RequestParam(value = "hostId", required = false) Long hostId)
}
@GetMapping("/{id}")
public @ResponseBody Scene findById(@PathVariable("id") long id, Principal principal)
throws NotFoundException { throws NotFoundException {
return sceneRepository if (hostId == null) {
.findByIdAndUsername(id, principal.getName()) return toList(sceneRepository.findByUsername(principal.getName()));
.orElseThrow(NotFoundException::new); } else {
Utils.returnIfGuest(userRepository, null, hostId, principal);
return sceneRepository.findByHostId(hostId);
}
} }
@PostMapping @PostMapping
@ -49,7 +41,7 @@ public class SceneController {
@Valid @RequestBody SceneSaveRequest s, final Principal principal) { @Valid @RequestBody SceneSaveRequest s, final Principal principal) {
final String username = principal.getName(); final String username = principal.getName();
final Long userId = userService.findByUsername(username).getId(); final Long userId = userRepository.findByUsername(username).getId();
final Scene newScene = new Scene(); final Scene newScene = new Scene();
@ -62,14 +54,27 @@ public class SceneController {
} }
@PostMapping("/{id}/apply") @PostMapping("/{id}/apply")
public @ResponseBody List<Device> apply(@PathVariable("id") long id, final Principal principal) public @ResponseBody List<Device> apply(
@PathVariable("id") long id,
final Principal principal,
@RequestParam(value = "hostId", required = false) Long hostId)
throws NotFoundException { throws NotFoundException {
final Scene newScene = if (hostId == null) {
return sceneService.apply(
sceneRepository sceneRepository
.findByIdAndUsername(id, principal.getName()) .findByIdAndUsername(id, principal.getName())
.orElseThrow(NotFoundException::new); .orElseThrow(NotFoundException::new),
principal.getName(),
return sceneService.apply(newScene, principal.getName(), false); false);
} else {
Utils.returnIfGuest(userRepository, null, hostId, principal);
return sceneService.applyAsGuest(
sceneRepository
.findByIdAndUserId(id, hostId)
.orElseThrow(NotFoundException::new),
principal.getName(),
hostId);
}
} }
@PostMapping("/{id}/copyFrom/{copyId}") @PostMapping("/{id}/copyFrom/{copyId}")
@ -112,7 +117,7 @@ public class SceneController {
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public void deleteById(@PathVariable("id") long id) { public void deleteById(@PathVariable("id") long id) {
stateService.deleteAllBySceneId(id); stateRepository.deleteAllBySceneId(id);
sceneRepository.deleteById(id); sceneRepository.deleteById(id);
} }
@ -122,7 +127,7 @@ public class SceneController {
*/ */
@GetMapping(path = "/{sceneId}/states") @GetMapping(path = "/{sceneId}/states")
public List<State<?>> getDevices(@PathVariable("sceneId") long sceneId) { public List<State<?>> getDevices(@PathVariable("sceneId") long sceneId) {
Iterable<State<?>> states = stateService.findBySceneId(sceneId); Iterable<State<?>> states = stateRepository.findBySceneId(sceneId);
return toList(states); return toList(states);
} }
} }

View file

@ -19,4 +19,9 @@ public interface SceneRepository extends CrudRepository<Scene, Long> {
@Query("SELECT s FROM Scene s JOIN s.user u WHERE u.username = ?1") @Query("SELECT s FROM Scene s JOIN s.user u WHERE u.username = ?1")
List<Scene> findByUsername(String username); List<Scene> findByUsername(String username);
@Query("SELECT s FROM Scene s JOIN s.user u WHERE u.id = ?1 AND s.guestAccessEnabled = true")
List<Scene> findByHostId(Long hostId);
Optional<Scene> findByIdAndUserId(Long id, Long userId);
} }

View file

@ -9,6 +9,8 @@ import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -68,6 +70,7 @@ public class DeviceService {
throws NotFoundException { throws NotFoundException {
try { try {
Iterable<Device> devices; Iterable<Device> devices;
User host = null;
if (hostId == null) { if (hostId == null) {
if (roomId != null) { if (roomId != null) {
roomRepository roomRepository
@ -79,8 +82,7 @@ public class DeviceService {
} }
} else { } else {
final User guest = userRepository.findByUsername(username); final User guest = userRepository.findByUsername(username);
final User host = host = userRepository.findById(hostId).orElseThrow(NotFoundException::new);
userRepository.findById(hostId).orElseThrow(NotFoundException::new);
if (!guest.getHosts().contains(host)) { if (!guest.getHosts().contains(host)) {
throw new NotFoundException(); throw new NotFoundException();
@ -99,7 +101,13 @@ public class DeviceService {
populateComputedFields(devices); populateComputedFields(devices);
if (host != null && !host.isCameraEnabled()) {
return StreamSupport.stream(devices.spliterator(), true)
.filter(d -> !(d instanceof SecurityCamera))
.collect(Collectors.toList());
} else {
return toList(devices); return toList(devices);
}
} catch (NotFoundException e) { } catch (NotFoundException e) {
e.printStackTrace(); e.printStackTrace();
throw e; throw e;
@ -120,7 +128,13 @@ public class DeviceService {
final User host = userRepository.findById(hostId).orElseThrow(NotFoundException::new); final User host = userRepository.findById(hostId).orElseThrow(NotFoundException::new);
if (!host.getGuests().contains(currentUser)) throw new NotFoundException(); if (!host.getGuests().contains(currentUser)) throw new NotFoundException();
renameIfDuplicate(device, host.getUsername()); renameIfDuplicate(device, host.getUsername());
device = deviceRepository.save(device); device = deviceRepository.save(device);
propagateUpdateAsGuest(device, host, currentUser);
return device;
}
private void propagateUpdateAsGuest(Device device, User host, User guest) {
final Set<User> guests = Set.copyOf(host.getGuests()); final Set<User> guests = Set.copyOf(host.getGuests());
// We're telling the host that a guest has modified a device. Therefore, fromGuest becomes // We're telling the host that a guest has modified a device. Therefore, fromGuest becomes
@ -130,15 +144,22 @@ public class DeviceService {
// We're telling all guests that a higher entity has issued a device update. Therefore, // We're telling all guests that a higher entity has issued a device update. Therefore,
// fromHost becomes true. // fromHost becomes true.
for (final User guest : guests) { for (final User aGuest : guests) {
if (guest.equals(currentUser)) { if (aGuest.equals(guest)) {
continue; continue;
} }
// enqueue all device updates for all other guests // enqueue all device updates for all other guests
endpoint.queueDeviceUpdate(device, guest, false, host.getId(), false); endpoint.queueDeviceUpdate(device, aGuest, false, host.getId(), false);
}
} }
return device; List<Device> saveAllAsGuestSceneApplication(
List<Device> devices, String guestUsername, Long hostId) {
final User guest = userRepository.findByUsername(guestUsername);
final User host = userRepository.findById(hostId).orElseThrow(IllegalStateException::new);
deviceRepository.saveAll(devices);
devices.forEach(d -> this.propagateUpdateAsGuest(d, host, guest));
return devices;
} }
/** /**

View file

@ -13,18 +13,30 @@ public class SceneService {
@Autowired private DeviceService deviceService; @Autowired private DeviceService deviceService;
@Autowired private StateRepository<State<?>> stateRepository; @Autowired private StateRepository<State<?>> stateRepository;
public List<Device> apply(Scene newScene, String username, boolean fromTrigger) { private List<Device> copyStatesToDevices(Scene fromScene) {
final List<Device> updated = new ArrayList<>(); final List<Device> updated = new ArrayList<>();
for (final State<?> s : newScene.getStates()) { for (final State<?> s : fromScene.getStates()) {
s.apply(); s.apply();
updated.add(s.getDevice()); updated.add(s.getDevice());
} }
deviceService.saveAllAsOwner(updated, username, true, fromTrigger);
deviceService.populateComputedFields(updated); deviceService.populateComputedFields(updated);
return updated; return updated;
} }
public List<Device> apply(Scene newScene, String username, boolean fromTrigger) {
List<Device> updated = copyStatesToDevices(newScene);
deviceService.saveAllAsOwner(updated, username, true, fromTrigger);
return updated;
}
public List<Device> applyAsGuest(Scene newScene, String username, Long hostId) {
List<Device> updated = copyStatesToDevices(newScene);
deviceService.saveAllAsGuestSceneApplication(updated, username, hostId);
return updated;
}
public List<State<?>> copyStates(Scene to, Scene from) { public List<State<?>> copyStates(Scene to, Scene from) {
final ArrayList<State<?>> states = new ArrayList<>(); final ArrayList<State<?>> states = new ArrayList<>();
for (final State<?> s : from.getStates()) { for (final State<?> s : from.getStates()) {