diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java index 67b25dc..08c3e60 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java @@ -23,15 +23,26 @@ public class GsonConfig { return converter; } - public static Gson gson() { + private static GsonBuilder configureBuilder() { final GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(Json.class, new SpringfoxJsonToGsonAdapter()); - builder.addSerializationExclusionStrategy(new AnnotationExclusionStrategy()); RuntimeTypeAdapterFactory runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory.of(State.class, "kind") .registerSubtype(SwitchableState.class, "switchableState") .registerSubtype(DimmableState.class, "dimmableState"); builder.registerTypeAdapterFactory(runtimeTypeAdapterFactory); + return builder; + } + + public static Gson gson() { + final GsonBuilder builder = configureBuilder(); + builder.addSerializationExclusionStrategy(new AnnotationExclusionStrategy()); + return builder.create(); + } + + public static Gson socketGson() { + final GsonBuilder builder = configureBuilder(); + builder.addSerializationExclusionStrategy(new SocketAnnotationExclusionStrategy()); return builder.create(); } } @@ -56,3 +67,16 @@ class AnnotationExclusionStrategy implements ExclusionStrategy { return false; } } + +/** GSON exclusion strategy to exclude attributes with @SocketGsonExclude */ +class SocketAnnotationExclusionStrategy implements ExclusionStrategy { + @Override + public boolean shouldSkipField(FieldAttributes f) { + return f.getAnnotation(SocketGsonExclude.class) != null; + } + + @Override + public boolean shouldSkipClass(Class clazz) { + return false; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SocketGsonExclude.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SocketGsonExclude.java new file mode 100644 index 0000000..e9806bc --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SocketGsonExclude.java @@ -0,0 +1,10 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface SocketGsonExclude {} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java index 2f45d22..d09adfb 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java @@ -81,6 +81,7 @@ public class SpringFoxConfig { .or(PathSelectors.regex("/switch.*")::apply) .or(PathSelectors.regex("/motionSensor.*")::apply) .or(PathSelectors.regex("/curtains.*")::apply) + .or(PathSelectors.regex("/user.*")::apply) .or(PathSelectors.regex("/auth/profile.*")::apply); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java index 1870abc..ad00070 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java @@ -1,13 +1,11 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; -import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; - import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.ButtonDimmerDimRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService; import java.security.Principal; -import java.util.List; import java.util.Set; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; @@ -19,37 +17,30 @@ import org.springframework.web.bind.annotation.*; @RequestMapping("/buttonDimmer") public class ButtonDimmerController extends InputDeviceConnectionController { + + private DeviceService deviceService; private ButtonDimmerRepository buttonDimmerRepository; private DimmableRepository dimmableRepository; @Autowired protected ButtonDimmerController( - ButtonDimmerRepository inputRepository, DimmableRepository outputRepository) { - super( - inputRepository, - outputRepository, - DimmableLight.BUTTON_DIMMER_DIMMABLE_CONNECTOR); + ButtonDimmerRepository inputRepository, + DimmableRepository outputRepository, + DeviceService deviceService) { + super(inputRepository, outputRepository, DimmableLight.BUTTON_DIMMER_DIMMABLE_CONNECTOR); + this.deviceService = deviceService; this.buttonDimmerRepository = inputRepository; this.dimmableRepository = outputRepository; } - @GetMapping - public List findAll() { - return toList(buttonDimmerRepository.findAll()); - } - - @GetMapping("/{id}") - public ButtonDimmer findById(@PathVariable("id") long id) throws NotFoundException { - return buttonDimmerRepository.findById(id).orElseThrow(NotFoundException::new); - } - @PostMapping - public ButtonDimmer create(@Valid @RequestBody final GenericDeviceSaveReguest bd) { + public ButtonDimmer create( + @Valid @RequestBody final GenericDeviceSaveReguest bd, final Principal principal) { ButtonDimmer newBD = new ButtonDimmer(); newBD.setName(bd.getName()); newBD.setRoomId(bd.getRoomId()); - return buttonDimmerRepository.save(newBD); + return deviceService.saveAsOwner(newBD, principal.getName()); } @PutMapping("/dim") @@ -76,7 +67,8 @@ public class ButtonDimmerController } @DeleteMapping("/{id}") - public void delete(@PathVariable("id") long id) { - buttonDimmerRepository.deleteById(id); + public void delete(@PathVariable("id") long id, final Principal principal) + throws NotFoundException { + deviceService.delete(id, principal.getName()); } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/CurtainsController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/CurtainsController.java index 5eb00be..517eea2 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/CurtainsController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/CurtainsController.java @@ -1,13 +1,11 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; -import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; - import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.DimmableSaveRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateStateException; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService; import java.security.Principal; -import java.util.List; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -17,44 +15,39 @@ import org.springframework.web.bind.annotation.*; @EnableAutoConfiguration @RequestMapping("/curtains") public class CurtainsController { + @Autowired private DeviceService deviceService; @Autowired private CurtainsRepository curtainsService; @Autowired private SceneRepository sceneRepository; @Autowired private StateRepository> stateRepository; - @GetMapping - public List findAll() { - return toList(curtainsService.findAll()); - } - - @GetMapping("/{id}") - public Curtains findById(@PathVariable("id") long id) throws NotFoundException { - return curtainsService.findById(id).orElseThrow(NotFoundException::new); - } - - private Curtains save(Curtains newRL, DimmableSaveRequest s) { + private Curtains save(Curtains newRL, DimmableSaveRequest s, final Principal principal) { newRL.setName(s.getName()); newRL.setRoomId(s.getRoomId()); newRL.setIntensity(s.getIntensity()); - return curtainsService.save(newRL); + return deviceService.saveAsOwner(newRL, principal.getName()); } @PostMapping - public Curtains create(@Valid @RequestBody DimmableSaveRequest curtain) { - return save(new Curtains(), curtain); + public Curtains create( + @Valid @RequestBody DimmableSaveRequest curtain, final Principal principal) { + return save(new Curtains(), curtain, principal); } @PutMapping - public Curtains update(@Valid @RequestBody DimmableSaveRequest curtain) + public Curtains update( + @Valid @RequestBody DimmableSaveRequest curtain, final Principal principal) throws NotFoundException { return save( curtainsService.findById(curtain.getId()).orElseThrow(NotFoundException::new), - curtain); + curtain, + principal); } @DeleteMapping("/{id}") - public void delete(@PathVariable("id") long id) { - curtainsService.deleteById(id); + public void delete(@PathVariable("id") long id, final Principal principal) + throws NotFoundException { + deviceService.delete(id, principal.getName()); } @PostMapping("/{id}/state") diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java index 17bdec7..b53b0af 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java @@ -6,6 +6,7 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Device; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DeviceRepository; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RoomRepository; +import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService; import java.security.Principal; import java.util.List; import javax.validation.Valid; @@ -18,12 +19,15 @@ import org.springframework.web.bind.annotation.*; @RequestMapping("/device") public class DeviceController { + @Autowired private DeviceService deviceService; @Autowired private DeviceRepository deviceRepository; @Autowired private RoomRepository roomRepository; @GetMapping - public List getAll(final Principal user) { - return deviceRepository.findAllByUsername(user.getName()); + public List getAll( + @RequestParam(value = "hostId", required = false) Long hostId, final Principal user) + throws NotFoundException { + return deviceService.findAll(hostId, user.getName()); } @PutMapping @@ -43,7 +47,7 @@ public class DeviceController { d.setRoomId(deviceSaveRequest.getRoomId()); d.setName(deviceSaveRequest.getName()); - deviceRepository.save(d); + deviceService.saveAsOwner(d, principal.getName()); return d; } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java index cb7cc00..277c367 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java @@ -1,13 +1,11 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; -import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; - import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.DimmableSaveRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateStateException; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService; import java.security.Principal; -import java.util.List; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -16,49 +14,71 @@ import org.springframework.web.bind.annotation.*; @RestController @EnableAutoConfiguration @RequestMapping("/dimmableLight") -public class DimmableLightController { +public class DimmableLightController extends GuestEnabledController { - @Autowired private DimmableLightRepository dimmableLightService; - @Autowired private SceneRepository sceneRepository; - @Autowired private StateRepository> stateRepository; + private DimmableLightRepository dimmableLightRepository; + private SceneRepository sceneRepository; + private StateRepository> stateRepository; + private DeviceService deviceService; - @GetMapping - public List findAll() { - return toList(dimmableLightService.findAll()); + @Autowired + public DimmableLightController( + UserRepository userRepository, + DimmableLightRepository dimmableLightRepository, + SceneRepository sceneRepository, + StateRepository> stateRepository, + DeviceService deviceService) { + super(userRepository, dimmableLightRepository); + this.dimmableLightRepository = dimmableLightRepository; + this.sceneRepository = sceneRepository; + this.stateRepository = stateRepository; + this.deviceService = deviceService; } - @GetMapping("/{id}") - public DimmableLight findById(@PathVariable("id") long id) throws NotFoundException { - return dimmableLightService.findById(id).orElseThrow(NotFoundException::new); - } - - private DimmableLight save(DimmableLight initial, DimmableSaveRequest dl) { + private DimmableLight save( + DimmableLight initial, DimmableSaveRequest dl, String username, Long hostId) + throws NotFoundException { initial.setIntensity(dl.getIntensity()); initial.setName(dl.getName()); initial.setRoomId(dl.getRoomId()); - return dimmableLightService.save(initial); + if (hostId == null) { + return deviceService.saveAsOwner(initial, username); + } else { + return deviceService.saveAsGuest(initial, username, hostId); + } } + /* + Assume that only the host can create a device + Here save always as host, but remember to propagate change to guests (DeviceService.saveAsOwner()) + */ @PostMapping - public DimmableLight create(@Valid @RequestBody DimmableSaveRequest dl) { - return save(new DimmableLight(), dl); + public DimmableLight create( + @Valid @RequestBody DimmableSaveRequest dl, final Principal principal) + throws NotFoundException { + return save(new DimmableLight(), dl, principal.getName(), null); } + /* + Logic for saving either as owner or guest is handled in method save of this controller + */ @PutMapping public DimmableLight update( - @Valid @RequestBody DimmableSaveRequest sp, final Principal principal) + @Valid @RequestBody DimmableSaveRequest sp, final Principal principal, Long hostId) throws NotFoundException { + return save( - dimmableLightService - .findByIdAndUsername(sp.getId(), principal.getName()) - .orElseThrow(NotFoundException::new), - sp); + fetchIfOwnerOrGuest(principal, sp.getId(), hostId), + sp, + principal.getName(), + hostId); } @DeleteMapping("/{id}") - public void delete(@PathVariable("id") long id) { - dimmableLightService.deleteById(id); + public void delete(@PathVariable("id") long id, final Principal principal) + throws NotFoundException { + deviceService.delete(id, principal.getName()); } // the full url should be: "/dimmableLight/{id}/state?sceneId={sceneId} @@ -71,7 +91,7 @@ public class DimmableLightController { throws NotFoundException, DuplicateStateException { DimmableLight d = - dimmableLightService + dimmableLightRepository .findByIdAndUsername(deviceId, principal.getName()) .orElseThrow(NotFoundException::new); State s = d.cloneState(); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/GuestController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/GuestController.java new file mode 100644 index 0000000..b0994a1 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/GuestController.java @@ -0,0 +1,59 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; + +import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; + +import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GuestPermissionsRequest; +import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository; +import java.security.Principal; +import java.util.List; +import javax.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.web.bind.annotation.*; + +@RestController +@EnableAutoConfiguration +@RequestMapping("/user") +public class GuestController { + + @Autowired private UserRepository userRepository; + + @GetMapping + public List findAll() { + return toList(userRepository.findAll()); + } + + @PostMapping("/guest") + public User addUserAsGuest(@RequestParam("userId") long id, final Principal principal) + throws NotFoundException { + User guest = userRepository.findById(id).orElseThrow(NotFoundException::new); + User host = userRepository.findByUsername(principal.getName()); + + host.addGuest(guest); + guest.addHost(host); + userRepository.save(guest); + return userRepository.save(host); + } + + @PutMapping("/permissions") + public User updatePermissions( + @Valid @RequestBody GuestPermissionsRequest g, final Principal principal) { + final User currentUser = userRepository.findByUsername(principal.getName()); + currentUser.setCameraEnabled(g.isCameraEnabled()); + return userRepository.save(currentUser); + } + + @DeleteMapping("/guest") + public void removeUserAsGuest(@RequestParam("userId") long id, final Principal principal) + throws NotFoundException { + User guest = userRepository.findById(id).orElseThrow(NotFoundException::new); + User host = userRepository.findByUsername(principal.getName()); + + host.removeGuest(guest); + guest.getHosts().remove(host); + userRepository.save(host); + userRepository.save(guest); + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/GuestEnabledController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/GuestEnabledController.java new file mode 100644 index 0000000..1aa2e7c --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/GuestEnabledController.java @@ -0,0 +1,37 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; + +import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.returnIfGuest; + +import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import java.security.Principal; + +public abstract class GuestEnabledController { + + private UserRepository userRepository; + private DeviceRepository deviceRepository; + + public GuestEnabledController( + final UserRepository userRepository, final DeviceRepository deviceRepository) { + this.userRepository = userRepository; + this.deviceRepository = deviceRepository; + } + + protected T fetchIfOwnerOrGuest(final Principal principal, Long id, Long hostId) + throws NotFoundException { + if (hostId == null) { + return deviceRepository + .findByIdAndUsername(id, principal.getName()) + .orElseThrow(NotFoundException::new); + } else { + /* + * Slightly less extremely verbose check through various repositories to control user/guest authorization. + */ + T device = + deviceRepository + .findByIdAndUserId(id, hostId) + .orElseThrow(NotFoundException::new); + return returnIfGuest(userRepository, device, hostId, principal); + } + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java index adf75ea..0f5d281 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java @@ -1,13 +1,11 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; -import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; - import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.KnobDimmerDimRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService; import java.security.Principal; -import java.util.List; import java.util.Set; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; @@ -17,40 +15,28 @@ import org.springframework.web.bind.annotation.*; @RestController @EnableAutoConfiguration @RequestMapping("/knobDimmer") -public class KnobDimmerController - extends InputDeviceConnectionController { +public class KnobDimmerController extends InputDeviceConnectionController { + @Autowired private DeviceService deviceService; @Autowired private KnobDimmerRepository knobDimmerRepository; @Autowired private DimmableRepository dimmableRepository; @Autowired protected KnobDimmerController( KnobDimmerRepository inputRepository, DimmableRepository outputRepository) { - super( - inputRepository, - outputRepository, - Dimmable.KNOB_DIMMER_DIMMABLE_CONNECTOR); + super(inputRepository, outputRepository, Dimmable.KNOB_DIMMER_DIMMABLE_CONNECTOR); this.knobDimmerRepository = inputRepository; this.dimmableRepository = outputRepository; } - @GetMapping - public List findAll() { - return toList(knobDimmerRepository.findAll()); - } - - @GetMapping("/{id}") - public KnobDimmer findById(@PathVariable("id") long id) throws NotFoundException { - return knobDimmerRepository.findById(id).orElseThrow(NotFoundException::new); - } - @PostMapping - public KnobDimmer create(@Valid @RequestBody GenericDeviceSaveReguest kd) { + public KnobDimmer create( + @Valid @RequestBody GenericDeviceSaveReguest kd, final Principal principal) { KnobDimmer newKD = new KnobDimmer(); newKD.setName(kd.getName()); newKD.setRoomId(kd.getRoomId()); - return knobDimmerRepository.save(newKD); + return deviceService.saveAsOwner(newKD, principal.getName()); } @PutMapping("/dimTo") @@ -69,7 +55,8 @@ public class KnobDimmerController } @DeleteMapping("/{id}") - public void delete(@PathVariable("id") long id) { - knobDimmerRepository.deleteById(id); + public void delete(@PathVariable("id") long id, final Principal principal) + throws NotFoundException { + deviceService.delete(id, principal.getName()); } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java index c349aa2..b674468 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java @@ -1,14 +1,12 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; -import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; - import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.MotionSensor; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.MotionSensorRepository; +import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService; import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint; import java.security.Principal; -import java.util.List; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -19,27 +17,20 @@ import org.springframework.web.bind.annotation.*; @RequestMapping("/motionSensor") public class MotionSensorController { + @Autowired private DeviceService deviceService; + @Autowired private MotionSensorRepository motionSensorService; @Autowired private SensorSocketEndpoint sensorSocketEndpoint; - @GetMapping - public List findAll() { - return toList(motionSensorService.findAll()); - } - - @GetMapping("/{id}") - public MotionSensor findById(@PathVariable("id") long id) throws NotFoundException { - return motionSensorService.findById(id).orElseThrow(NotFoundException::new); - } - @PostMapping - public MotionSensor create(@Valid @RequestBody GenericDeviceSaveReguest ms) { + public MotionSensor create( + @Valid @RequestBody GenericDeviceSaveReguest ms, final Principal principal) { MotionSensor newMS = new MotionSensor(); newMS.setName(ms.getName()); newMS.setRoomId(ms.getRoomId()); - return motionSensorService.save(newMS); + return deviceService.saveAsOwner(newMS, principal.getName()); } /** @@ -53,7 +44,8 @@ public class MotionSensorController { sensor.setDetected(detected); final MotionSensor toReturn = motionSensorService.save(sensor); - sensorSocketEndpoint.queueDeviceUpdate(sensor, motionSensorService.findUser(sensor.getId())); + sensorSocketEndpoint.queueDeviceUpdate( + sensor, motionSensorService.findUser(sensor.getId())); return toReturn; } @@ -73,7 +65,8 @@ public class MotionSensorController { } @DeleteMapping("/{id}") - public void delete(@PathVariable("id") long id) { - motionSensorService.deleteById(id); + public void delete(@PathVariable("id") long id, final Principal principal) + throws NotFoundException { + deviceService.delete(id, principal.getName()); } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java index ac0fd47..47d98d0 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java @@ -6,6 +6,7 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SwitchableSaveRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateStateException; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService; import java.security.Principal; import java.util.List; import javax.validation.Valid; @@ -24,52 +25,76 @@ import org.springframework.web.bind.annotation.RestController; @RestController @EnableAutoConfiguration @RequestMapping("/regularLight") -public class RegularLightController { +public class RegularLightController extends GuestEnabledController { - @Autowired private RegularLightRepository regularLightService; - @Autowired private SceneRepository sceneRepository; - @Autowired private StateRepository> stateRepository; + private RegularLightRepository regularLightRepository; + private SceneRepository sceneRepository; + private StateRepository> stateRepository; + private DeviceService deviceService; + + @Autowired + public RegularLightController( + UserRepository userRepository, + RegularLightRepository regularLightRepository, + SceneRepository sceneRepository, + StateRepository> stateRepository, + DeviceService deviceService) { + super(userRepository, regularLightRepository); + this.regularLightRepository = regularLightRepository; + this.sceneRepository = sceneRepository; + this.stateRepository = stateRepository; + this.deviceService = deviceService; + } @GetMapping public List findAll() { - return toList(regularLightService.findAll()); + return toList(regularLightRepository.findAll()); } @GetMapping("/{id}") public RegularLight findById(@PathVariable("id") long id) throws NotFoundException { - return regularLightService.findById(id).orElseThrow(NotFoundException::new); + return regularLightRepository.findById(id).orElseThrow(NotFoundException::new); } - private RegularLight save(RegularLight newRL, SwitchableSaveRequest rl) { - newRL.setName(rl.getName()); - newRL.setRoomId(rl.getRoomId()); - newRL.setOn(rl.isOn()); + private RegularLight save( + RegularLight initial, SwitchableSaveRequest rl, String username, Long hostId) + throws NotFoundException { + initial.setName(rl.getName()); + initial.setRoomId(rl.getRoomId()); + initial.setOn(rl.isOn()); - return regularLightService.save(newRL); + if (hostId == null) { + return deviceService.saveAsOwner(initial, username); + } else { + return deviceService.saveAsGuest(initial, username, hostId); + } } @PostMapping - public RegularLight create(@Valid @RequestBody SwitchableSaveRequest rl) { - return save(new RegularLight(), rl); + public RegularLight create( + @Valid @RequestBody SwitchableSaveRequest rl, final Principal principal) + throws NotFoundException { + return save(new RegularLight(), rl, principal.getName(), null); } @PutMapping public RegularLight update( - @Valid @RequestBody SwitchableSaveRequest rl, final Principal principal) + @Valid @RequestBody SwitchableSaveRequest rl, final Principal principal, Long hostId) throws NotFoundException { return save( - regularLightService - .findByIdAndUsername(rl.getId(), principal.getName()) - .orElseThrow(NotFoundException::new), - rl); + fetchIfOwnerOrGuest(principal, rl.getId(), hostId), + rl, + principal.getName(), + hostId); } @DeleteMapping("/{id}") - public void delete(@PathVariable("id") long id) { - regularLightService.deleteById(id); + public void delete(@PathVariable("id") long id, final Principal principal) + throws NotFoundException { + deviceService.delete(id, principal.getName()); } - // the full url should be: "/dimmableLight/{id}/state?sceneId={sceneId} + // the full url should be: "/regularLight/{id}/state?sceneId={sceneId} // however it is not necessary to specify the query in the mapping @PostMapping("/{id}/state") public State sceneBinding( @@ -78,7 +103,7 @@ public class RegularLightController { final Principal principal) throws NotFoundException, DuplicateStateException { RegularLight d = - regularLightService + regularLightRepository .findByIdAndUsername(deviceId, principal.getName()) .orElseThrow(NotFoundException::new); State s = d.cloneState(); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java index 9474fd4..136446a 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java @@ -5,7 +5,9 @@ import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.RoomSaveRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService; import ch.usi.inf.sa4.sanmarinoes.smarthut.service.ThermostatService; +import ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils; import java.security.Principal; import java.util.*; import javax.validation.Valid; @@ -22,7 +24,7 @@ public class RoomController { @Autowired private UserRepository userRepository; - @Autowired private DeviceRepository deviceRepository; + @Autowired private DeviceService deviceService; @Autowired private SwitchRepository switchRepository; @@ -32,14 +34,38 @@ public class RoomController { @Autowired private ThermostatService thermostatService; + private List fetchOwnerOrGuest( + final List list, Long hostId, final Principal principal) throws NotFoundException { + if (hostId == null) { + return list; + } else { + return Utils.returnIfGuest(userRepository, list, hostId, principal); + } + } + @GetMapping - public List findAll() { - return toList(roomRepository.findAll()); + public List findAll( + @RequestParam(value = "hostId", required = false) Long hostId, + final Principal principal) + throws NotFoundException { + + List rooms = toList(roomRepository.findAll()); + return fetchOwnerOrGuest(rooms, hostId, principal); } @GetMapping("/{id}") - public @ResponseBody Room findById(@PathVariable("id") long id) throws NotFoundException { - return roomRepository.findById(id).orElseThrow(NotFoundException::new); + public @ResponseBody Room findById( + @PathVariable("id") long id, + final Principal principal, + @RequestParam(value = "hostId", required = false) Long hostId) + throws NotFoundException { + Room room = roomRepository.findById(id).orElseThrow(NotFoundException::new); + /* Very ugly way of avoiding code duplication. If this method call throws no exception, + * we can return the room safely. I pass null as I do not return a list in this case. + * Refer to fetchOwnerOrGuest for further information. + */ + fetchOwnerOrGuest(null, hostId, principal); + return room; } @PostMapping @@ -101,13 +127,11 @@ public class RoomController { * id). */ @GetMapping(path = "/{roomId}/devices") - public List getDevices(@PathVariable("roomId") long roomid) { - Iterable devices = deviceRepository.findByRoomId(roomid); - for (Device d : devices) { - if (d instanceof Thermostat) { - thermostatService.populateMeasuredTemperature((Thermostat) d); - } - } - return toList(devices); + public List getDevices( + @PathVariable("roomId") long roomId, + final Principal principal, + @RequestParam(value = "hostId", required = false) Long hostId) + throws NotFoundException { + return deviceService.findAll(roomId, hostId, principal.getName()); } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java index e9174af..fc2268d 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java @@ -56,6 +56,7 @@ public class SceneController { newScene.setUserId(userId); newScene.setName(s.getName()); + newScene.setGuestAccessEnabled(s.isGuestAccessEnabled()); return sceneRepository.save(newScene); } @@ -84,6 +85,8 @@ public class SceneController { newScene.setName(s.getName()); } + newScene.setGuestAccessEnabled(s.isGuestAccessEnabled()); + return sceneRepository.save(newScene); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SecurityCameraController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SecurityCameraController.java index d7fdc8e..c2d955c 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SecurityCameraController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SecurityCameraController.java @@ -1,18 +1,15 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; -import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; - import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SwitchableSaveRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateStateException; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService; import java.security.Principal; -import java.util.List; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; @@ -26,31 +23,24 @@ import org.springframework.web.bind.annotation.RestController; @RequestMapping("/securityCamera") public class SecurityCameraController { + @Autowired DeviceService deviceService; @Autowired SecurityCameraRepository securityCameraService; @Autowired private SceneRepository sceneRepository; @Autowired private StateRepository> stateRepository; - @GetMapping - public List findAll() { - return toList(securityCameraService.findAll()); - } - - @GetMapping("/{id}") - public SecurityCamera findById(@PathVariable("id") long id) throws NotFoundException { - return securityCameraService.findById(id).orElseThrow(NotFoundException::new); - } - - private SecurityCamera save(SecurityCamera newSC, SwitchableSaveRequest sc) { + private SecurityCamera save( + SecurityCamera newSC, SwitchableSaveRequest sc, final Principal principal) { newSC.setName(sc.getName()); newSC.setRoomId(sc.getRoomId()); newSC.setOn(sc.isOn()); - return securityCameraService.save(newSC); + return deviceService.saveAsOwner(newSC, principal.getName()); } @PostMapping - public SecurityCamera create(@Valid @RequestBody SwitchableSaveRequest sc) { - return save(new SecurityCamera(), sc); + public SecurityCamera create( + @Valid @RequestBody SwitchableSaveRequest sc, final Principal principal) { + return save(new SecurityCamera(), sc, principal); } @PutMapping @@ -61,12 +51,14 @@ public class SecurityCameraController { securityCameraService .findByIdAndUsername(sc.getId(), principal.getName()) .orElseThrow(NotFoundException::new), - sc); + sc, + principal); } @DeleteMapping("/{id}") - public void delete(@PathVariable("id") long id) { - securityCameraService.deleteById(id); + public void delete(@PathVariable("id") long id, final Principal principal) + throws NotFoundException { + deviceService.delete(id, principal.getName()); } @PostMapping("/{id}/state") diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java index b56fd60..ea01909 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java @@ -1,19 +1,15 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; -import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; - import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SensorSaveRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService; import ch.usi.inf.sa4.sanmarinoes.smarthut.service.SensorService; import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint; - import java.math.BigDecimal; import java.security.Principal; import java.util.*; -import java.util.List; import javax.validation.Valid; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.*; import org.springframework.web.bind.annotation.*; @@ -23,34 +19,23 @@ import org.springframework.web.bind.annotation.*; @RequestMapping("/sensor") public class SensorController { - @Autowired - private SensorRepository sensorRepository; + @Autowired private DeviceService deviceService; - @Autowired - private SensorSocketEndpoint sensorSocketEndpoint; + @Autowired private SensorRepository sensorRepository; - @Autowired - private SensorService sensorService; + @Autowired private SensorSocketEndpoint sensorSocketEndpoint; - @GetMapping - public List findAll() { - return toList(sensorRepository.findAll()); - } - - @GetMapping("/{id}") - public Sensor findById(@PathVariable("id") long id) throws NotFoundException { - return sensorRepository.findById(id).orElseThrow(NotFoundException::new); - } + @Autowired private SensorService sensorService; @PostMapping - public Sensor create(@Valid @RequestBody SensorSaveRequest s) { + public Sensor create(@Valid @RequestBody SensorSaveRequest s, final Principal principal) { Sensor newSensor = new Sensor(); newSensor.setSensor(s.getSensor()); newSensor.setName(s.getName()); newSensor.setRoomId(s.getRoomId()); newSensor.setValue(s.getValue()); - return sensorRepository.save(newSensor); + return deviceService.saveAsOwner(newSensor, principal.getName()); } @PutMapping("/{id}/value") @@ -67,7 +52,8 @@ public class SensorController { } @DeleteMapping("/{id}") - public void deleteById(@PathVariable("id") long id) { - sensorRepository.deleteById(id); + public void deleteById(@PathVariable("id") long id, final Principal principal) + throws NotFoundException { + deviceService.delete(id, principal.getName()); } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java index d90c9ac..308836c 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java @@ -1,13 +1,11 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; -import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; - import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SwitchableSaveRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateStateException; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService; import java.security.Principal; -import java.util.List; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.*; @@ -18,32 +16,24 @@ import org.springframework.web.bind.annotation.*; @RequestMapping("/smartPlug") public class SmartPlugController { + @Autowired private DeviceService deviceService; @Autowired private SmartPlugRepository smartPlugRepository; @Autowired private SceneRepository sceneRepository; @Autowired private StateRepository> stateRepository; - @GetMapping - public List findAll() { - return toList(smartPlugRepository.findAll()); - } - - @GetMapping("/{id}") - public SmartPlug findById(@PathVariable("id") long id) throws NotFoundException { - return smartPlugRepository.findById(id).orElseThrow(NotFoundException::new); - } - - private SmartPlug save(SmartPlug newSP, SwitchableSaveRequest sp) { + private SmartPlug save(SmartPlug newSP, SwitchableSaveRequest sp, final Principal principal) { newSP.setOn(sp.isOn()); newSP.setId(sp.getId()); newSP.setName(sp.getName()); newSP.setRoomId(sp.getRoomId()); - return smartPlugRepository.save(newSP); + return deviceService.saveAsOwner(newSP, principal.getName()); } @PostMapping - public SmartPlug create(@Valid @RequestBody SwitchableSaveRequest sp) { - return save(new SmartPlug(), sp); + public SmartPlug create( + @Valid @RequestBody SwitchableSaveRequest sp, final Principal principal) { + return save(new SmartPlug(), sp, principal); } @PutMapping @@ -53,7 +43,8 @@ public class SmartPlugController { smartPlugRepository .findByIdAndUsername(sp.getId(), principal.getName()) .orElseThrow(NotFoundException::new), - sp); + sp, + principal); } @DeleteMapping("/{id}/meter") @@ -69,8 +60,9 @@ public class SmartPlugController { } @DeleteMapping("/{id}") - public void deleteById(@PathVariable("id") long id) { - smartPlugRepository.deleteById(id); + public void deleteById(@PathVariable("id") long id, final Principal principal) + throws NotFoundException { + deviceService.delete(id, principal.getName()); } @PostMapping("/{id}/state") diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java index 3bf41b8..9b95451 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java @@ -1,14 +1,11 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; -import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; - import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SwitchOperationRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService; import java.security.Principal; -import java.util.*; import java.util.List; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; @@ -21,6 +18,7 @@ import org.springframework.web.bind.annotation.*; public class SwitchController extends InputDeviceConnectionController { private SwitchRepository switchRepository; + private SwitchableRepository switchableRepository; private DeviceService deviceService; /** @@ -35,27 +33,18 @@ public class SwitchController extends InputDeviceConnectionController outputRepository, DeviceService deviceService) { super(inputRepository, outputRepository, Switchable.SWITCH_SWITCHABLE_CONNECTOR); - this.switchRepository = inputRepository; this.deviceService = deviceService; - } - - @GetMapping - public List findAll() { - return toList(switchRepository.findAll()); - } - - @GetMapping("/{id}") - public Switch findById(@PathVariable("id") long id) throws NotFoundException { - return switchRepository.findById(id).orElseThrow(NotFoundException::new); + this.switchRepository = inputRepository; } @PostMapping - public Switch create(@Valid @RequestBody GenericDeviceSaveReguest s) { + public Switch create( + @Valid @RequestBody GenericDeviceSaveReguest s, final Principal principal) { Switch newSwitch = new Switch(); newSwitch.setName(s.getName()); newSwitch.setRoomId(s.getRoomId()); - return switchRepository.save(newSwitch); + return deviceService.saveAsOwner(newSwitch, principal.getName()); } @PutMapping("/operate") @@ -79,12 +68,13 @@ public class SwitchController extends InputDeviceConnectionController> stateRepository; - @GetMapping - public List findAll(Principal user) { - return thermostatService.findAll(user.getName()); - } - - @GetMapping("/{id}") - public Thermostat findById(@PathVariable("id") long id, Principal principal) - throws NotFoundException { - return thermostatService - .findById(id, principal.getName()) - .orElseThrow(NotFoundException::new); - } - - private Thermostat save(Thermostat newT, ThermostatSaveRequest t) { + private Thermostat save(Thermostat newT, ThermostatSaveRequest t, final Principal principal) { newT.setTargetTemperature(t.getTargetTemperature()); newT.setId(t.getId()); newT.setName(t.getName()); @@ -45,14 +31,15 @@ public class ThermostatController { newT.setUseExternalSensors(t.isUseExternalSensors()); newT.setOn(t.isTurnOn()); - newT = thermostatRepository.save(newT); + newT = deviceService.saveAsOwner(newT, principal.getName()); thermostatService.populateMeasuredTemperature(newT); return newT; } @PostMapping - public Thermostat create(@Valid @RequestBody ThermostatSaveRequest t) { - return save(new Thermostat(), t); + public Thermostat create( + @Valid @RequestBody ThermostatSaveRequest t, final Principal principal) { + return save(new Thermostat(), t, principal); } @PutMapping @@ -62,12 +49,14 @@ public class ThermostatController { thermostatRepository .findByIdAndUsername(t.getId(), principal.getName()) .orElseThrow(NotFoundException::new), - t); + t, + principal); } @DeleteMapping("/{id}") - public void deleteById(@PathVariable("id") long id) { - thermostatRepository.deleteById(id); + public void deleteById(@PathVariable("id") long id, final Principal principal) + throws NotFoundException { + deviceService.delete(id, principal.getName()); } @PostMapping("/{id}/state") diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/GuestPermissionsRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/GuestPermissionsRequest.java new file mode 100644 index 0000000..8c1a2c4 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/GuestPermissionsRequest.java @@ -0,0 +1,13 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; + +public class GuestPermissionsRequest { + private boolean cameraEnabled; + + public boolean isCameraEnabled() { + return cameraEnabled; + } + + public void setCameraEnabled(boolean cameraEnabled) { + this.cameraEnabled = cameraEnabled; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SceneSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SceneSaveRequest.java index ea3c6bd..080ea2b 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SceneSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SceneSaveRequest.java @@ -1,6 +1,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; import com.sun.istack.NotNull; +import javax.persistence.Column; public class SceneSaveRequest { @@ -10,6 +11,17 @@ public class SceneSaveRequest { /** The user given name of this room (e.g. 'Master bedroom') */ @NotNull private String name; + /** Determines whether a guest can access this scene */ + @Column @NotNull private boolean guestAccessEnabled; + + public boolean isGuestAccessEnabled() { + return guestAccessEnabled; + } + + public void setGuestAccessEnabled(boolean guestAccessEnabled) { + this.guestAccessEnabled = guestAccessEnabled; + } + public long getId() { return id; } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java index 1787a67..a84e705 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java @@ -1,6 +1,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude; +import ch.usi.inf.sa4.sanmarinoes.smarthut.config.SocketGsonExclude; import com.google.gson.annotations.SerializedName; import io.swagger.annotations.ApiModelProperty; import java.util.HashSet; @@ -32,6 +33,7 @@ public abstract class Device { @ManyToOne @JoinColumn(name = "room_id", updatable = false, insertable = false) @GsonExclude + @SocketGsonExclude private Room room; @OneToMany(mappedBy = "device", orphanRemoval = true) @@ -66,7 +68,38 @@ public abstract class Device { @OneToMany(mappedBy = "device", orphanRemoval = true) @GsonExclude - private Set states = new HashSet<>(); + @SocketGsonExclude + private Set> states = new HashSet<>(); + + @Transient @GsonExclude private boolean fromHost = false; + + @Transient @GsonExclude private boolean fromGuest = false; + + @Transient @GsonExclude private boolean deleted = false; + + public boolean isFromHost() { + return fromHost; + } + + public boolean isDeleted() { + return deleted; + } + + public void setDeleted(boolean deleted) { + this.deleted = deleted; + } + + public boolean isFromGuest() { + return fromGuest; + } + + public void setFromGuest(boolean fromGuest) { + this.fromGuest = fromGuest; + } + + public void setFromHost(boolean fromHost) { + this.fromHost = fromHost; + } public long getId() { return id; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DeviceRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DeviceRepository.java index 2496029..cf5c004 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DeviceRepository.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DeviceRepository.java @@ -25,6 +25,16 @@ public interface DeviceRepository extends CrudRepository findByIdAndUsername(Long id, String username); + /** + * Finds devices by their id and a user id + * + * @param id the device id + * @param userId a User's id + * @return an optional device, empty if none found + */ + @Query("SELECT d FROM Device d JOIN d.room r JOIN r.user u WHERE d.id = ?1 AND u.id = ?2") + Optional findByIdAndUserId(Long id, Long userId); + /** * Finds all devices belonging to a user * diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java index 4f0f592..34f3824 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java @@ -145,7 +145,6 @@ public class Room { */ @NotNull @Column(name = "user_id", nullable = false) - @GsonExclude private Long userId; /** The user given name of this room (e.g. 'Master bedroom') */ diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Scene.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Scene.java index c4b6213..f35d54d 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Scene.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Scene.java @@ -39,6 +39,17 @@ public class Scene { @Column(nullable = false) private String name; + /** Determines whether a guest can access this scene */ + @Column private boolean guestAccessEnabled; + + public boolean isGuestAccessEnabled() { + return guestAccessEnabled; + } + + public void setGuestAccessEnabled(boolean guestAccessEnabled) { + this.guestAccessEnabled = guestAccessEnabled; + } + public String getName() { return name; } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java index 60aad17..b58e383 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java @@ -2,7 +2,9 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude; import io.swagger.annotations.ApiModelProperty; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; import javax.persistence.*; /** A user of the Smarthut application */ @@ -35,6 +37,23 @@ public class User { @Column(nullable = false, unique = true) private String email; + /** Guests invited by this user */ + @ManyToMany(mappedBy = "hosts", cascade = CascadeType.DETACH) + @GsonExclude + private Set guests = new HashSet<>(); + + @ManyToMany(cascade = CascadeType.DETACH) + @JoinTable( + name = "invited", + joinColumns = @JoinColumn(name = "guest_id"), + inverseJoinColumns = @JoinColumn(name = "host_id")) + @GsonExclude + private Set hosts = new HashSet<>(); + + /** Determines whether a guest can access security cameras */ + @Column(nullable = false) + private boolean cameraEnabled; + @Column(nullable = false) @GsonExclude private Boolean isEnabled = false; @@ -87,10 +106,37 @@ public class User { isEnabled = enabled; } + public Set getGuests() { + return guests; + } + + public Set getHosts() { + return hosts; + } + + public boolean isCameraEnabled() { + return cameraEnabled; + } + + public void addGuest(User guest) { + this.guests.add(guest); + } + + public void addHost(User host) { + this.hosts.add(host); + } + + public void removeGuest(User guest) { + this.guests.remove(guest); + } + + public void setCameraEnabled(boolean cameraEnabled) { + this.cameraEnabled = cameraEnabled; + } + @Override public String toString() { - return "User{" - + "id=" + return "User{id=" + id + ", name='" + name @@ -104,6 +150,8 @@ public class User { + ", email='" + email + '\'' + + ", cameraEnabled=" + + cameraEnabled + ", isEnabled=" + isEnabled + '}'; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/DeviceService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/DeviceService.java index 137d0e3..923c21e 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/DeviceService.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/DeviceService.java @@ -1,10 +1,14 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.service; +import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; + +import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint; import java.util.Collection; import java.util.Comparator; import java.util.List; -import java.util.stream.Collectors; +import java.util.Set; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -16,12 +20,12 @@ public class DeviceService { @Autowired private SceneRepository sceneRepository; @Autowired private SceneService sceneService; @Autowired private TriggerRepository> triggerRepository; + @Autowired private RoomRepository roomRepository; + @Autowired private UserRepository userRepository; + @Autowired private SensorSocketEndpoint endpoint; + @Autowired private ThermostatService thermostatService; - public List saveAll(Collection devices) { - return devices.stream().map(this::save).collect(Collectors.toList()); - } - - public T save(T device) { + public void triggerTriggers(Device device) { final long deviceId = device.getId(); @@ -42,12 +46,120 @@ public class DeviceService { .findById(t.getSceneId()) .orElseThrow(IllegalStateException::new)) .forEach(sceneService::apply); + } - // map activated -> automations - // remove duplicates - // sort scenes inside automations per priority - // apply scenes (SceneService.apply()) + public List findAll(Long hostId, String username) throws NotFoundException { + return findAll(null, hostId, username); + } - return deviceRepository.save(device); + public List findAll(Long roomId, Long hostId, String username) + throws NotFoundException { + try { + Iterable devices; + if (hostId == null) { + if (roomId != null) { + roomRepository + .findByIdAndUsername(roomId, username) + .orElseThrow(NotFoundException::new); + devices = deviceRepository.findByRoomId(roomId); + } else { + devices = deviceRepository.findAllByUsername(username); + } + } else { + final User guest = userRepository.findByUsername(username); + final User host = + userRepository.findById(hostId).orElseThrow(NotFoundException::new); + + if (!guest.getHosts().contains(host)) { + throw new NotFoundException(); + } + + if (roomId != null) { + Room r = roomRepository.findById(roomId).orElseThrow(NotFoundException::new); + if (!r.getUserId().equals(hostId)) { + throw new NotFoundException(); + } + devices = deviceRepository.findByRoomId(roomId); + } else { + devices = deviceRepository.findAllByUsername(host.getUsername()); + } + } + + for (Device d : devices) { + if (d instanceof Thermostat) { + thermostatService.populateMeasuredTemperature((Thermostat) d); + } + } + + return toList(devices); + } catch (NotFoundException e) { + e.printStackTrace(); + throw e; + } + } + + public T saveAsGuest(T device, String guestUsername, Long hostId) + throws NotFoundException { + device = deviceRepository.save(device); + + final User currentUser = userRepository.findByUsername(guestUsername); + final User host = userRepository.findById(hostId).orElseThrow(NotFoundException::new); + final Set guests = Set.copyOf(host.getGuests()); + + // We're telling the host that a guest has modified a device. Therefore, fromGuest becomes + // true. + device.setFromHost(false); + device.setFromGuest(true); + // broadcast device update to host + endpoint.queueDeviceUpdate(device, host); + + // We're telling all guests that a higher entity has issued a device update. Therefore, + // fromHost becomes true. + device.setFromHost(true); + device.setFromGuest(false); + for (final User guest : guests) { + if (guest.equals(currentUser)) { + continue; + } + // enqueue all device updates for all other guests + endpoint.queueDeviceUpdate(device, guest); + } + + return device; + } + + public T saveAsOwner(T device, String username) { + device = deviceRepository.save(device); + + final User user = userRepository.findByUsername(username); + final Set guests = user.getGuests(); + // make sure we're broadcasting from host + device.setFromHost(true); + device.setFromGuest(false); + for (final User guest : guests) { + // broadcast to endpoint the object device, with receiving user set to guest + endpoint.queueDeviceUpdate(device, guest); + } + + return device; + } + + public void delete(Long id, String username) throws NotFoundException { + Device device = + deviceRepository + .findByIdAndUsername(id, username) + .orElseThrow(NotFoundException::new); + deviceRepository.delete(device); + + final User user = userRepository.findByUsername(username); + final Set guests = user.getGuests(); + + device.setFromHost(true); + device.setFromGuest(false); + device.setDeleted(true); + for (final User guest : guests) { + // broadcast to endpoint the object device, with receiving user set to guest + endpoint.queueDeviceUpdate(device, guest); + } } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java index b222cf5..b68cb25 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java @@ -9,25 +9,17 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.gson.Gson; - import java.io.IOException; import java.util.*; import javax.websocket.*; - -import com.google.gson.JsonObject; -import io.jsonwebtoken.ExpiredJwtException; -import org.hibernate.annotations.common.reflection.XProperty; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.parameters.P; import org.springframework.stereotype.Component; -/** - * Endpoint of socket at URL /sensor-socket used to update the client with sensor information - */ +/** Endpoint of socket at URL /sensor-socket used to update the client with sensor information */ @Component public class SensorSocketEndpoint extends Endpoint { - private Gson gson = GsonConfig.gson(); + private Gson gson = GsonConfig.socketGson(); private UserRepository userRepository; @@ -46,6 +38,7 @@ public class SensorSocketEndpoint extends Endpoint { /** * Queues a single device update for a certain user to be sent + * * @param device the device update to be sent * @param u the user the device belongs */ @@ -56,9 +49,7 @@ public class SensorSocketEndpoint extends Endpoint { } } - /** - * Sends all device updates queued to be sent in a unique WebSocket message - */ + /** Sends all device updates queued to be sent in a unique WebSocket message */ public void flushDeviceUpdates() { synchronized (messages) { for (Map.Entry> batchForUser : messages.entrySet()) { @@ -69,11 +60,11 @@ public class SensorSocketEndpoint extends Endpoint { } /** - * Given a collection of messages and a user, broadcasts that message in json to all - * associated clients + * Given a collection of messages and a user, broadcasts that message in json to all associated + * clients * * @param messages the message batch to send - * @param u the user to which to send the message + * @param u the user to which to send the message */ private void broadcast(User u, Collection messages) { if (messages.isEmpty()) return; @@ -95,7 +86,7 @@ public class SensorSocketEndpoint extends Endpoint { * Handles the opening of a socket session with a client * * @param session the newly born session - * @param config endpoint configuration + * @param config endpoint configuration */ @Override public void onOpen(Session session, EndpointConfig config) { diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java index 319a8e3..050afa5 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java @@ -1,7 +1,10 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.utils; +import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository; +import java.security.Principal; import java.util.List; -import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @@ -9,24 +12,19 @@ import java.util.stream.StreamSupport; public final class Utils { private Utils() {} - @FunctionalInterface - public interface ConsumerWithException { - void apply(T input) throws Throwable; + public static U returnIfGuest( + UserRepository userRepository, U toReturn, Long hostId, Principal principal) + throws NotFoundException { + User host = userRepository.findById(hostId).orElseThrow(NotFoundException::new); + User guest = userRepository.findByUsername(principal.getName()); + if (!host.getGuests().contains(guest)) { + throw new NotFoundException(); + } else { + return toReturn; + } } public static List toList(Iterable iterable) { return StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList()); } - - public static Predicate didThrow(ConsumerWithException consumer) { - return (t) -> { - try { - consumer.apply(t); - return true; - } catch (Throwable e) { - System.err.println(e.getMessage()); - return false; - } - }; - } }