Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
d4a0f195ad
29 changed files with 381 additions and 257 deletions
|
@ -52,14 +52,10 @@ test:
|
||||||
reports:
|
reports:
|
||||||
junit: build/test-results/test/TEST-*.xml
|
junit: build/test-results/test/TEST-*.xml
|
||||||
|
|
||||||
#Runs a quality check on the code and creates a report on the codes
|
sonarqube:
|
||||||
code_quality:
|
image: gradle:jdk11
|
||||||
stage: code_quality
|
stage: code_quality
|
||||||
allow_failure: true
|
only:
|
||||||
|
- dev
|
||||||
script:
|
script:
|
||||||
- gradle cpdCheck
|
- gradle build jacocoTestReport sonarqube -Dsonar.verbose=true -Dsonar.host.url=$SONAR_URL -Dsonar.login=$SONAR_LOGIN -Dsonar.projectKey=$CI_PROJECT_PATH_SLUG -Dsonar.projectName=$CI_PROJECT_PATH_SLUG -Dsonar.scm.disabled=True -Dsonar.coverage.jacoco.xmlReportPaths=./build/reports/jacoco/test/jacocoTestReport.xml
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- build/reports/cpd/cpdCheck.xml
|
|
||||||
#create a report on the quality of the code
|
|
||||||
expose_as: 'Code Quality Report'
|
|
||||||
|
|
8
.mailmap
Normal file
8
.mailmap
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
Claudio Maggioni <maggicl@usi.ch> Claudio Maggioni (maggicl) <maggicl@kolabnow.ch>
|
||||||
|
Claudio Maggioni <maggicl@usi.ch> Claudio Maggioni (maggicl) <maggicl@usi.ch>
|
||||||
|
Filippo Cesana <cesanf@usi.ch> FilippoCesana <cesanf@usi.ch>
|
||||||
|
Filippo Cesana <cesanf@usi.ch> Fil Cesana <cesanf@usi.ch>
|
||||||
|
Andrea Brites Marto <britea@usi.ch> britea <andreabritesma@gmail.com>
|
||||||
|
Christian Capeáns Pérez <capeac@usi.ch> christiancp <capeac@usi.ch>
|
||||||
|
Tommaso Rodolfo Masera <rodolt@usi.ch> tommi27 <tommi99@hotmail.it>
|
||||||
|
Matteo Omenetti <omenem@usi.ch> omenem <omenem@usi.ch>
|
13
build.gradle
13
build.gradle
|
@ -1,8 +1,9 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'org.springframework.boot' version '2.2.4.RELEASE'
|
id 'org.springframework.boot' version '2.2.4.RELEASE'
|
||||||
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
|
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
|
||||||
id "de.aaschmid.cpd" version "3.1"
|
|
||||||
id 'java'
|
id 'java'
|
||||||
|
id 'jacoco'
|
||||||
|
id "org.sonarqube" version "2.8"
|
||||||
}
|
}
|
||||||
group = 'ch.usi.inf.sa4.sanmarinoes'
|
group = 'ch.usi.inf.sa4.sanmarinoes'
|
||||||
version = '0.0.1-SNAPSHOT'
|
version = '0.0.1-SNAPSHOT'
|
||||||
|
@ -50,3 +51,13 @@ gradle.projectsEvaluated {
|
||||||
test {
|
test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jacocoTestReport {
|
||||||
|
reports {
|
||||||
|
xml.enabled true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins.withType(JacocoPlugin) {
|
||||||
|
tasks["test"].finalizedBy 'jacocoTestReport'
|
||||||
|
}
|
||||||
|
|
4
gradle.properties
Normal file
4
gradle.properties
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
systemProp.sonar.host.url=https://lab.si.usi.ch:9000
|
||||||
|
systemProp.sonar.login=871fdfcb09345b1841f1730596ac32aacf3a86fb
|
||||||
|
systemProp.sonar.projectKey=SMASmarthutBackend
|
||||||
|
systemProp.sonar.scm.disabled=true
|
|
@ -46,67 +46,67 @@ public class EmailConfigurationService {
|
||||||
|
|
||||||
@NotNull private String registrationRedirect;
|
@NotNull private String registrationRedirect;
|
||||||
|
|
||||||
public String getRegistrationSubject() {
|
public synchronized String getRegistrationSubject() {
|
||||||
return registrationSubject;
|
return registrationSubject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRegistrationSubject(String registrationSubject) {
|
public synchronized void setRegistrationSubject(String registrationSubject) {
|
||||||
this.registrationSubject = registrationSubject;
|
this.registrationSubject = registrationSubject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRegistration() {
|
public synchronized String getRegistration() {
|
||||||
return registration;
|
return registration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRegistration(String registration) {
|
public synchronized void setRegistration(String registration) {
|
||||||
this.registration = registration;
|
this.registration = registration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRegistrationPath() {
|
public synchronized String getRegistrationPath() {
|
||||||
return registrationPath;
|
return registrationPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRegistrationPath(String registrationPath) {
|
public synchronized void setRegistrationPath(String registrationPath) {
|
||||||
this.registrationPath = registrationPath;
|
this.registrationPath = registrationPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getResetPasswordSubject() {
|
public synchronized String getResetPasswordSubject() {
|
||||||
return resetPasswordSubject;
|
return resetPasswordSubject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setResetPasswordSubject(String resetPasswordSubject) {
|
public synchronized void setResetPasswordSubject(String resetPasswordSubject) {
|
||||||
this.resetPasswordSubject = resetPasswordSubject;
|
this.resetPasswordSubject = resetPasswordSubject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getResetPassword() {
|
public synchronized String getResetPassword() {
|
||||||
return resetPassword;
|
return resetPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setResetPassword(String resetPassword) {
|
public synchronized void setResetPassword(String resetPassword) {
|
||||||
this.resetPassword = resetPassword;
|
this.resetPassword = resetPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getResetPasswordPath() {
|
public synchronized String getResetPasswordPath() {
|
||||||
return resetPasswordPath;
|
return resetPasswordPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setResetPasswordPath(String resetPasswordPath) {
|
public synchronized void setResetPasswordPath(String resetPasswordPath) {
|
||||||
this.resetPasswordPath = resetPasswordPath;
|
this.resetPasswordPath = resetPasswordPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getResetPasswordRedirect() {
|
public synchronized String getResetPasswordRedirect() {
|
||||||
return resetPasswordRedirect;
|
return resetPasswordRedirect;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setResetPasswordRedirect(String resetPasswordRedirect) {
|
public synchronized void setResetPasswordRedirect(String resetPasswordRedirect) {
|
||||||
this.resetPasswordRedirect = resetPasswordRedirect;
|
this.resetPasswordRedirect = resetPasswordRedirect;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRegistrationRedirect() {
|
public synchronized String getRegistrationRedirect() {
|
||||||
return registrationRedirect;
|
return registrationRedirect;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRegistrationRedirect(String registrationRedirect) {
|
public synchronized void setRegistrationRedirect(String registrationRedirect) {
|
||||||
this.registrationRedirect = registrationRedirect;
|
this.registrationRedirect = registrationRedirect;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,9 +233,9 @@ public final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
final Map<String, TypeAdapter<?>> labelToDelegate =
|
final Map<String, TypeAdapter<?>> labelToDelegate =
|
||||||
new LinkedHashMap<String, TypeAdapter<?>>();
|
new LinkedHashMap<String, TypeAdapter<?>>(labelToSubtype.size());
|
||||||
final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate =
|
final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate =
|
||||||
new LinkedHashMap<Class<?>, TypeAdapter<?>>();
|
new LinkedHashMap<Class<?>, TypeAdapter<?>>(labelToSubtype.size());
|
||||||
for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
|
for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
|
||||||
TypeAdapter<?> delegate =
|
TypeAdapter<?> delegate =
|
||||||
gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
|
gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
|
||||||
|
@ -245,7 +245,7 @@ public final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
|
||||||
|
|
||||||
return new TypeAdapter<R>() {
|
return new TypeAdapter<R>() {
|
||||||
@Override
|
@Override
|
||||||
public R read(JsonReader in) throws IOException {
|
public R read(JsonReader in) {
|
||||||
JsonElement jsonElement = Streams.parse(in);
|
JsonElement jsonElement = Streams.parse(in);
|
||||||
JsonElement labelJsonElement;
|
JsonElement labelJsonElement;
|
||||||
if (maintainType) {
|
if (maintainType) {
|
||||||
|
|
|
@ -6,16 +6,14 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTResponse;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UnauthorizedException;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UnauthorizedException;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UserNotFoundException;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UserNotFoundException;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.JWTUserDetailsService;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.JWTUserDetailsService;
|
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.authentication.DisabledException;
|
import org.springframework.security.authentication.DisabledException;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@ -30,8 +28,6 @@ public class AuthenticationController {
|
||||||
|
|
||||||
private final JWTUserDetailsService userDetailsService;
|
private final JWTUserDetailsService userDetailsService;
|
||||||
|
|
||||||
private BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
|
||||||
|
|
||||||
public AuthenticationController(
|
public AuthenticationController(
|
||||||
AuthenticationManager authenticationManager,
|
AuthenticationManager authenticationManager,
|
||||||
UserRepository userRepository,
|
UserRepository userRepository,
|
||||||
|
@ -82,9 +78,9 @@ public class AuthenticationController {
|
||||||
authenticationManager.authenticate(
|
authenticationManager.authenticate(
|
||||||
new UsernamePasswordAuthenticationToken(username, password));
|
new UsernamePasswordAuthenticationToken(username, password));
|
||||||
} catch (DisabledException e) {
|
} catch (DisabledException e) {
|
||||||
throw new UnauthorizedException(true);
|
throw new UnauthorizedException(true, e);
|
||||||
} catch (BadCredentialsException e) {
|
} catch (BadCredentialsException e) {
|
||||||
throw new UnauthorizedException(false);
|
throw new UnauthorizedException(false, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,22 @@ public abstract class InputDeviceConnectionController<
|
||||||
this.input = input;
|
this.input = input;
|
||||||
this.outputs = outputs;
|
this.outputs = outputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public I getInput() {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<O> getOutputs() {
|
||||||
|
return outputs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DeviceRepository<I> getInputRepository() {
|
||||||
|
return inputRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DeviceRepository<O> getOutputReposiory() {
|
||||||
|
return outputReposiory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired private DeviceService deviceService;
|
@Autowired private DeviceService deviceService;
|
||||||
|
@ -65,7 +81,7 @@ public abstract class InputDeviceConnectionController<
|
||||||
inputRepository
|
inputRepository
|
||||||
.findByIdAndUsername(inputId, username)
|
.findByIdAndUsername(inputId, username)
|
||||||
.orElseThrow(() -> new NotFoundException("input device"));
|
.orElseThrow(() -> new NotFoundException("input device"));
|
||||||
final List<O> outputDevices = new ArrayList<>();
|
final List<O> outputDevices = new ArrayList<>(outputs.size());
|
||||||
for (final Long outputId : outputs) {
|
for (final Long outputId : outputs) {
|
||||||
outputDevices.add(
|
outputDevices.add(
|
||||||
outputReposiory
|
outputReposiory
|
||||||
|
@ -87,12 +103,12 @@ public abstract class InputDeviceConnectionController<
|
||||||
Long inputId, List<Long> outputs, String username) throws NotFoundException {
|
Long inputId, List<Long> outputs, String username) throws NotFoundException {
|
||||||
final Connection pair = checkConnectionIDs(inputId, outputs, username);
|
final Connection pair = checkConnectionIDs(inputId, outputs, username);
|
||||||
|
|
||||||
for (final O o : pair.outputs) {
|
for (final O o : pair.getOutputs()) {
|
||||||
connector.connect(pair.input, o, true);
|
connector.connect(pair.getInput(), o, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceService.saveAllAsOwner(pair.outputs, username);
|
deviceService.saveAllAsOwner(pair.getOutputs(), username);
|
||||||
return pair.input.getOutputs();
|
return pair.getInput().getOutputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,12 +123,12 @@ public abstract class InputDeviceConnectionController<
|
||||||
Long inputId, List<Long> outputs, String username) throws NotFoundException {
|
Long inputId, List<Long> outputs, String username) throws NotFoundException {
|
||||||
final Connection pair = checkConnectionIDs(inputId, outputs, username);
|
final Connection pair = checkConnectionIDs(inputId, outputs, username);
|
||||||
|
|
||||||
for (final O o : pair.outputs) {
|
for (final O o : pair.getOutputs()) {
|
||||||
connector.connect(pair.input, o, false);
|
connector.connect(pair.getInput(), o, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceService.saveAllAsOwner(pair.outputs, username);
|
deviceService.saveAllAsOwner(pair.getOutputs(), username);
|
||||||
return pair.input.getOutputs();
|
return pair.getInput().getOutputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/lights")
|
@PostMapping("/{id}/lights")
|
||||||
|
|
|
@ -17,14 +17,17 @@ import org.springframework.web.bind.annotation.*;
|
||||||
@RequestMapping("/knobDimmer")
|
@RequestMapping("/knobDimmer")
|
||||||
public class KnobDimmerController extends InputDeviceConnectionController<KnobDimmer, Dimmable> {
|
public class KnobDimmerController extends InputDeviceConnectionController<KnobDimmer, Dimmable> {
|
||||||
|
|
||||||
@Autowired private DeviceService deviceService;
|
private final DeviceService deviceService;
|
||||||
@Autowired private KnobDimmerRepository knobDimmerRepository;
|
private final KnobDimmerRepository knobDimmerRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected KnobDimmerController(
|
protected KnobDimmerController(
|
||||||
KnobDimmerRepository inputRepository, DimmableRepository<Dimmable> outputRepository) {
|
KnobDimmerRepository inputRepository,
|
||||||
|
DimmableRepository<Dimmable> outputRepository,
|
||||||
|
DeviceService deviceService) {
|
||||||
super(inputRepository, outputRepository, Dimmable.KNOB_DIMMER_DIMMABLE_CONNECTOR);
|
super(inputRepository, outputRepository, Dimmable.KNOB_DIMMER_DIMMABLE_CONNECTOR);
|
||||||
this.knobDimmerRepository = inputRepository;
|
this.knobDimmerRepository = inputRepository;
|
||||||
|
this.deviceService = deviceService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
|
|
|
@ -70,7 +70,7 @@ public class SceneController {
|
||||||
Utils.returnIfGuest(userRepository, null, hostId, principal);
|
Utils.returnIfGuest(userRepository, null, hostId, principal);
|
||||||
return sceneService.applyAsGuest(
|
return sceneService.applyAsGuest(
|
||||||
sceneRepository
|
sceneRepository
|
||||||
.findByIdAndUserId(id, hostId)
|
.findByIdAndUserIdAndGuestAccessEnabled(id, hostId, true)
|
||||||
.orElseThrow(NotFoundException::new),
|
.orElseThrow(NotFoundException::new),
|
||||||
principal.getName(),
|
principal.getName(),
|
||||||
hostId);
|
hostId);
|
||||||
|
|
|
@ -18,7 +18,6 @@ import org.springframework.web.bind.annotation.*;
|
||||||
public class SwitchController extends InputDeviceConnectionController<Switch, Switchable> {
|
public class SwitchController extends InputDeviceConnectionController<Switch, Switchable> {
|
||||||
|
|
||||||
private SwitchRepository switchRepository;
|
private SwitchRepository switchRepository;
|
||||||
private SwitchableRepository<Switchable> switchableRepository;
|
|
||||||
private DeviceService deviceService;
|
private DeviceService deviceService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -36,8 +36,7 @@ public class ThermostatController {
|
||||||
newT = thermostatRepository.save(newT);
|
newT = thermostatRepository.save(newT);
|
||||||
|
|
||||||
newT.setOn(t.isTurnOn());
|
newT.setOn(t.isTurnOn());
|
||||||
newT = deviceService.saveAsOwner(newT, principal.getName());
|
return deviceService.saveAsOwner(newT, principal.getName());
|
||||||
return newT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
|
|
|
@ -2,7 +2,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
|
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.EmailConfigurationService;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.EmailConfigurationService;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.InitPasswordResetRequest;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.InitPasswordResetRequest;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse;
|
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.PasswordResetRequest;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.PasswordResetRequest;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateRegistrationException;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateRegistrationException;
|
||||||
|
@ -78,7 +77,7 @@ public class UserAccountController {
|
||||||
* @throws DuplicateRegistrationException if a user exists with same email or username
|
* @throws DuplicateRegistrationException if a user exists with same email or username
|
||||||
*/
|
*/
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public OkResponse registerUser(@Valid @RequestBody UserRegistrationRequest registrationData)
|
public void registerUser(@Valid @RequestBody UserRegistrationRequest registrationData)
|
||||||
throws DuplicateRegistrationException {
|
throws DuplicateRegistrationException {
|
||||||
final User existingEmailUser =
|
final User existingEmailUser =
|
||||||
userRepository.findByEmailIgnoreCase(registrationData.getEmail());
|
userRepository.findByEmailIgnoreCase(registrationData.getEmail());
|
||||||
|
@ -112,8 +111,6 @@ public class UserAccountController {
|
||||||
confirmationTokenRepository.save(token);
|
confirmationTokenRepository.save(token);
|
||||||
|
|
||||||
sendEmail(toSave.getEmail(), token, true);
|
sendEmail(toSave.getEmail(), token, true);
|
||||||
|
|
||||||
return new OkResponse();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +122,7 @@ public class UserAccountController {
|
||||||
* @throws UserNotFoundException if given email does not belong to any user
|
* @throws UserNotFoundException if given email does not belong to any user
|
||||||
*/
|
*/
|
||||||
@PostMapping("/init-reset-password")
|
@PostMapping("/init-reset-password")
|
||||||
public OkResponse initResetPassword(@Valid @RequestBody InitPasswordResetRequest resetRequest)
|
public void initResetPassword(@Valid @RequestBody InitPasswordResetRequest resetRequest)
|
||||||
throws UserNotFoundException {
|
throws UserNotFoundException {
|
||||||
final User toReset = userRepository.findByEmailIgnoreCase(resetRequest.getEmail());
|
final User toReset = userRepository.findByEmailIgnoreCase(resetRequest.getEmail());
|
||||||
|
|
||||||
|
@ -148,8 +145,6 @@ public class UserAccountController {
|
||||||
confirmationTokenRepository.save(token);
|
confirmationTokenRepository.save(token);
|
||||||
|
|
||||||
sendEmail(toReset.getEmail(), token, false);
|
sendEmail(toReset.getEmail(), token, false);
|
||||||
|
|
||||||
return new OkResponse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -160,7 +155,7 @@ public class UserAccountController {
|
||||||
* @throws EmailTokenNotFoundException if given token is not a valid token for password reset
|
* @throws EmailTokenNotFoundException if given token is not a valid token for password reset
|
||||||
*/
|
*/
|
||||||
@PutMapping("/reset-password")
|
@PutMapping("/reset-password")
|
||||||
public OkResponse resetPassword(
|
public void resetPassword(
|
||||||
@Valid @RequestBody PasswordResetRequest resetRequest,
|
@Valid @RequestBody PasswordResetRequest resetRequest,
|
||||||
final HttpServletResponse response)
|
final HttpServletResponse response)
|
||||||
throws EmailTokenNotFoundException, IOException {
|
throws EmailTokenNotFoundException, IOException {
|
||||||
|
@ -178,8 +173,6 @@ public class UserAccountController {
|
||||||
|
|
||||||
// Delete token to prevent further password changes
|
// Delete token to prevent further password changes
|
||||||
confirmationTokenRepository.delete(token);
|
confirmationTokenRepository.delete(token);
|
||||||
|
|
||||||
return new OkResponse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
|
||||||
|
|
||||||
/** A dummy DTO to return when there is no data to return */
|
|
||||||
public class OkResponse {
|
|
||||||
private boolean success = true;
|
|
||||||
}
|
|
|
@ -16,4 +16,16 @@ public class UserResponse {
|
||||||
us.username = u.getUsername();
|
us.username = u.getUsername();
|
||||||
return us;
|
return us;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
public class UnauthorizedException extends Exception {
|
public class UnauthorizedException extends Exception {
|
||||||
private final boolean isUserDisabled;
|
private final boolean isUserDisabled;
|
||||||
|
|
||||||
public UnauthorizedException(boolean isDisabled) {
|
public UnauthorizedException(boolean isDisabled, Throwable cause) {
|
||||||
super("Access denied: " + (isDisabled ? "user is disabled" : "wrong credentials"));
|
super("Access denied: " + (isDisabled ? "user is disabled" : "wrong credentials"), cause);
|
||||||
this.isUserDisabled = isDisabled;
|
this.isUserDisabled = isDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ public class ConfirmationToken {
|
||||||
private User user;
|
private User user;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private Boolean resetPassword;
|
private boolean resetPassword;
|
||||||
|
|
||||||
public ConfirmationToken(User user) {
|
public ConfirmationToken(User user) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
|
@ -76,11 +76,11 @@ public class ConfirmationToken {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getResetPassword() {
|
public boolean getResetPassword() {
|
||||||
return resetPassword;
|
return resetPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setResetPassword(Boolean resetPassword) {
|
public void setResetPassword(boolean resetPassword) {
|
||||||
this.resetPassword = resetPassword;
|
this.resetPassword = resetPassword;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,5 +23,6 @@ public interface SceneRepository extends CrudRepository<Scene, Long> {
|
||||||
@Query("SELECT s FROM Scene s JOIN s.user u WHERE u.id = ?1 AND s.guestAccessEnabled = true")
|
@Query("SELECT s FROM Scene s JOIN s.user u WHERE u.id = ?1 AND s.guestAccessEnabled = true")
|
||||||
List<Scene> findByHostId(Long hostId);
|
List<Scene> findByHostId(Long hostId);
|
||||||
|
|
||||||
Optional<Scene> findByIdAndUserId(Long id, Long userId);
|
Optional<Scene> findByIdAndUserIdAndGuestAccessEnabled(
|
||||||
|
Long id, Long userId, boolean guestAccessEnabled);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,9 @@ public class Sensor extends InputDevice implements RangeTriggerable {
|
||||||
|
|
||||||
public static final Map<SensorType, BigDecimal> TYPICAL_VALUES =
|
public static final Map<SensorType, BigDecimal> TYPICAL_VALUES =
|
||||||
Map.of(
|
Map.of(
|
||||||
SensorType.TEMPERATURE, new BigDecimal(17.0),
|
SensorType.TEMPERATURE, BigDecimal.valueOf(17.0),
|
||||||
SensorType.HUMIDITY, new BigDecimal(40.0),
|
SensorType.HUMIDITY, BigDecimal.valueOf(40.0),
|
||||||
SensorType.LIGHT, new BigDecimal(1000));
|
SensorType.LIGHT, BigDecimal.valueOf(1000));
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double readTriggerState() {
|
public double readTriggerState() {
|
||||||
|
|
|
@ -6,11 +6,9 @@ import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Transient;
|
import javax.persistence.Transient;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
/** A thermostat capable of controlling cooling and heating. */
|
/** A thermostat capable of controlling cooling and heating. */
|
||||||
@Entity
|
@Entity
|
||||||
@Component
|
|
||||||
public class Thermostat extends Switchable implements BooleanTriggerable {
|
public class Thermostat extends Switchable implements BooleanTriggerable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class User {
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
@GsonExclude
|
@GsonExclude
|
||||||
private Boolean isEnabled = false;
|
private boolean isEnabled = false;
|
||||||
|
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
return id;
|
return id;
|
||||||
|
@ -98,11 +98,11 @@ public class User {
|
||||||
this.password = password;
|
this.password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getEnabled() {
|
public boolean getEnabled() {
|
||||||
return isEnabled;
|
return isEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEnabled(Boolean enabled) {
|
public void setEnabled(boolean enabled) {
|
||||||
isEnabled = enabled;
|
isEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,16 +162,17 @@ public class User {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
User user = (User) o;
|
User user = (User) o;
|
||||||
return id.equals(user.id)
|
return cameraEnabled == user.cameraEnabled
|
||||||
&& name.equals(user.name)
|
&& isEnabled == user.isEnabled
|
||||||
&& username.equals(user.username)
|
&& Objects.equals(id, user.id)
|
||||||
&& password.equals(user.password)
|
&& Objects.equals(name, user.name)
|
||||||
&& email.equals(user.email)
|
&& Objects.equals(username, user.username)
|
||||||
&& isEnabled.equals(user.isEnabled);
|
&& Objects.equals(password, user.password)
|
||||||
|
&& Objects.equals(email, user.email);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(id, name, username, password, email, isEnabled);
|
return Objects.hash(id, name, username, password, email, isEnabled, cameraEnabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.service;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Device;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Thermostat;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class DevicePopulationService {
|
||||||
|
|
||||||
|
@Autowired private ThermostatService thermostatService;
|
||||||
|
|
||||||
|
public void populateComputedFields(Iterable<Device> devices) {
|
||||||
|
for (Device d : devices) {
|
||||||
|
if (d instanceof Thermostat) {
|
||||||
|
thermostatService.populateMeasuredTemperature((Thermostat) d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
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.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class DevicePropagationService {
|
||||||
|
|
||||||
|
@Autowired private SensorSocketEndpoint endpoint;
|
||||||
|
@Autowired private EagerUserRepository userRepository;
|
||||||
|
@Autowired private DeviceRepository<Device> deviceRepository;
|
||||||
|
|
||||||
|
void propagateUpdateAsGuest(Device device, User host, User guest) {
|
||||||
|
final Set<User> guests = Set.copyOf(host.getGuests());
|
||||||
|
|
||||||
|
// We're telling the host that a guest has modified a device. Therefore, fromGuest becomes
|
||||||
|
// true.
|
||||||
|
// broadcast device update to host
|
||||||
|
endpoint.queueDeviceUpdate(device, host, true, null, false);
|
||||||
|
|
||||||
|
// We're telling all guests that a higher entity has issued a device update. Therefore,
|
||||||
|
// fromHost becomes true.
|
||||||
|
for (final User aGuest : guests) {
|
||||||
|
if (aGuest.equals(guest)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// enqueue all device updates for all other guests
|
||||||
|
endpoint.queueDeviceUpdate(device, aGuest, false, host.getId(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
void renameIfDuplicate(Device toCreate, String username) {
|
||||||
|
while (deviceRepository.findDuplicates(toCreate.getName(), username)
|
||||||
|
- (toCreate.getId() <= 0 ? 0 : 1)
|
||||||
|
> 0) {
|
||||||
|
toCreate.setName(toCreate.getName() + " (new)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Device> T saveAsGuest(T device, String guestUsername, Long hostId)
|
||||||
|
throws NotFoundException {
|
||||||
|
final User currentUser = userRepository.findByUsername(guestUsername);
|
||||||
|
final User host = userRepository.findById(hostId).orElseThrow(NotFoundException::new);
|
||||||
|
if (!host.getGuests().contains(currentUser)) throw new NotFoundException();
|
||||||
|
renameIfDuplicate(device, host.getUsername());
|
||||||
|
|
||||||
|
device = deviceRepository.save(device);
|
||||||
|
propagateUpdateAsGuest(device, host, currentUser);
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves all the devices given in devices assuming that the owner updated them in one way or
|
||||||
|
* another. Takes care of the appropriate websocket updates and trigger checking as well. No
|
||||||
|
* checking is done to verify that the user whose username is given is in fact the owner of
|
||||||
|
* these devices
|
||||||
|
*
|
||||||
|
* @param devices the list of devices to save
|
||||||
|
* @param username the username of the owner of these devices
|
||||||
|
* @param fromScene true if the update comes from the a scene application side effect. Disables
|
||||||
|
* trigger checking to avoid recursive invocations of automations
|
||||||
|
* @param fromTrigger true if the update comes from a scene application executed by an
|
||||||
|
* automation. Propagates updated through socket to owner as well. No effect if fromScene is
|
||||||
|
* false.
|
||||||
|
* @param <T> the type of device contained in the list
|
||||||
|
* @return the updated list of devices, ready to be fed to GSON
|
||||||
|
*/
|
||||||
|
public <T extends Device> List<T> saveAllAsOwner(
|
||||||
|
Iterable<T> devices, String username, boolean fromScene, boolean fromTrigger) {
|
||||||
|
devices.forEach(d -> renameIfDuplicate(d, username));
|
||||||
|
devices = deviceRepository.saveAll(devices);
|
||||||
|
devices.forEach((d) -> propagateUpdateAsOwner(d, username, fromScene && fromTrigger));
|
||||||
|
|
||||||
|
return toList(devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Device> List<T> saveAllAsOwner(Iterable<T> devices, String username) {
|
||||||
|
return saveAllAsOwner(devices, username, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Device> T saveAsOwner(T device, String username) {
|
||||||
|
renameIfDuplicate(device, username);
|
||||||
|
device = deviceRepository.save(device);
|
||||||
|
propagateUpdateAsOwner(device, username, false);
|
||||||
|
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteByIdAsOwner(Long id, String username) throws NotFoundException {
|
||||||
|
Device d =
|
||||||
|
deviceRepository
|
||||||
|
.findByIdAndUsername(id, username)
|
||||||
|
.orElseThrow(NotFoundException::new);
|
||||||
|
|
||||||
|
final User user = userRepository.findByUsername(username);
|
||||||
|
final Set<User> guests = user.getGuests();
|
||||||
|
// make sure we're broadcasting from host
|
||||||
|
for (final User guest : guests) {
|
||||||
|
// broadcast to endpoint the object device, with receiving user set to guest
|
||||||
|
endpoint.queueDeviceUpdate(d, guest, false, user.getId(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceRepository.delete(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Propagates the update through the socket assuming that the user that modified the device is
|
||||||
|
* the owner of that device
|
||||||
|
*
|
||||||
|
* @param device the updated device
|
||||||
|
* @param username the username of the owner of that device
|
||||||
|
* @param causedByTrigger if true, send the update to the owner as well
|
||||||
|
*/
|
||||||
|
private void propagateUpdateAsOwner(Device device, String username, boolean causedByTrigger) {
|
||||||
|
final User user = userRepository.findByUsername(username);
|
||||||
|
final Set<User> guests = user.getGuests();
|
||||||
|
// make sure we're broadcasting from host
|
||||||
|
for (final User guest : guests) {
|
||||||
|
// broadcast to endpoint the object device, with receiving user set to guest
|
||||||
|
endpoint.queueDeviceUpdate(device, guest, false, user.getId(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (causedByTrigger) {
|
||||||
|
endpoint.queueDeviceUpdate(device, user, false, user.getId(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,11 +4,9 @@ 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.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.socket.SensorSocketEndpoint;
|
|
||||||
import java.util.Collection;
|
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.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -17,28 +15,42 @@ import org.springframework.stereotype.Component;
|
||||||
@Component
|
@Component
|
||||||
public class DeviceService {
|
public class DeviceService {
|
||||||
|
|
||||||
@Autowired private DeviceRepository<Device> deviceRepository;
|
private final DeviceRepository<Device> deviceRepository;
|
||||||
@Autowired private AutomationRepository automationRepository;
|
private final AutomationRepository automationRepository;
|
||||||
@Autowired private SceneRepository sceneRepository;
|
private final SceneRepository sceneRepository;
|
||||||
@Autowired private SceneService sceneService;
|
private final SceneService sceneService;
|
||||||
@Autowired private TriggerRepository<Trigger<? extends Device>> triggerRepository;
|
private final TriggerRepository<Trigger<? extends Device>> triggerRepository;
|
||||||
@Autowired private RoomRepository roomRepository;
|
private final RoomRepository roomRepository;
|
||||||
@Autowired private EagerUserRepository userRepository;
|
private final EagerUserRepository userRepository;
|
||||||
@Autowired private SensorSocketEndpoint endpoint;
|
private final DevicePopulationService devicePopulationService;
|
||||||
@Autowired private ThermostatService thermostatService;
|
private final DevicePropagationService devicePropagationService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public DeviceService(
|
||||||
|
DeviceRepository<Device> deviceRepository,
|
||||||
|
AutomationRepository automationRepository,
|
||||||
|
SceneRepository sceneRepository,
|
||||||
|
SceneService sceneService,
|
||||||
|
TriggerRepository<Trigger<? extends Device>> triggerRepository,
|
||||||
|
RoomRepository roomRepository,
|
||||||
|
EagerUserRepository userRepository,
|
||||||
|
DevicePopulationService devicePopulationService,
|
||||||
|
DevicePropagationService devicePropagationService) {
|
||||||
|
this.deviceRepository = deviceRepository;
|
||||||
|
this.automationRepository = automationRepository;
|
||||||
|
this.sceneRepository = sceneRepository;
|
||||||
|
this.sceneService = sceneService;
|
||||||
|
this.triggerRepository = triggerRepository;
|
||||||
|
this.roomRepository = roomRepository;
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.devicePopulationService = devicePopulationService;
|
||||||
|
this.devicePropagationService = devicePropagationService;
|
||||||
|
}
|
||||||
|
|
||||||
public void throwIfRoomNotOwned(Long roomId, String username) throws NotFoundException {
|
public void throwIfRoomNotOwned(Long roomId, String username) throws NotFoundException {
|
||||||
roomRepository.findByIdAndUsername(roomId, username).orElseThrow(NotFoundException::new);
|
roomRepository.findByIdAndUsername(roomId, username).orElseThrow(NotFoundException::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renameIfDuplicate(Device toCreate, String username) {
|
|
||||||
while (deviceRepository.findDuplicates(toCreate.getName(), username)
|
|
||||||
- (toCreate.getId() <= 0 ? 0 : 1)
|
|
||||||
> 0) {
|
|
||||||
toCreate.setName(toCreate.getName() + " (new)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void triggerTriggers(Device device, final String username) {
|
private void triggerTriggers(Device device, final String username) {
|
||||||
|
|
||||||
final long deviceId = device.getId();
|
final long deviceId = device.getId();
|
||||||
|
@ -66,122 +78,24 @@ public class DeviceService {
|
||||||
return findAll(null, hostId, username);
|
return findAll(null, hostId, username);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Device> findAll(Long roomId, Long hostId, String username)
|
|
||||||
throws NotFoundException {
|
|
||||||
try {
|
|
||||||
Iterable<Device> devices;
|
|
||||||
User host = null;
|
|
||||||
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);
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
} catch (NotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void populateComputedFields(Iterable<Device> devices) {
|
|
||||||
for (Device d : devices) {
|
|
||||||
if (d instanceof Thermostat) {
|
|
||||||
thermostatService.populateMeasuredTemperature((Thermostat) d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends Device> T saveAsGuest(T device, String guestUsername, Long hostId)
|
public <T extends Device> T saveAsGuest(T device, String guestUsername, Long hostId)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
final User currentUser = userRepository.findByUsername(guestUsername);
|
final User currentUser = userRepository.findByUsername(guestUsername);
|
||||||
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());
|
devicePropagationService.renameIfDuplicate(device, host.getUsername());
|
||||||
|
|
||||||
device = deviceRepository.save(device);
|
device = deviceRepository.save(device);
|
||||||
propagateUpdateAsGuest(device, host, currentUser);
|
devicePropagationService.propagateUpdateAsGuest(device, host, currentUser);
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void propagateUpdateAsGuest(Device device, User host, User guest) {
|
public void deleteByIdAsOwner(Long id, String username) throws NotFoundException {
|
||||||
final Set<User> guests = Set.copyOf(host.getGuests());
|
devicePropagationService.deleteByIdAsOwner(id, username);
|
||||||
|
|
||||||
// We're telling the host that a guest has modified a device. Therefore, fromGuest becomes
|
|
||||||
// true.
|
|
||||||
// broadcast device update to host
|
|
||||||
endpoint.queueDeviceUpdate(device, host, true, null, false);
|
|
||||||
|
|
||||||
// We're telling all guests that a higher entity has issued a device update. Therefore,
|
|
||||||
// fromHost becomes true.
|
|
||||||
for (final User aGuest : guests) {
|
|
||||||
if (aGuest.equals(guest)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// enqueue all device updates for all other guests
|
|
||||||
endpoint.queueDeviceUpdate(device, aGuest, false, host.getId(), false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Device> saveAllAsGuestSceneApplication(
|
public void populateComputedFields(Iterable<Device> devices) {
|
||||||
List<Device> devices, String guestUsername, Long hostId) {
|
devicePopulationService.populateComputedFields(devices);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Propagates the update through the socket assuming that the user that modified the device is
|
|
||||||
* the owner of that device
|
|
||||||
*
|
|
||||||
* @param device the updated device
|
|
||||||
* @param username the username of the owner of that device
|
|
||||||
* @param causedByTrigger if true, send the update to the owner as well
|
|
||||||
*/
|
|
||||||
private void propagateUpdateAsOwner(Device device, String username, boolean causedByTrigger) {
|
|
||||||
final User user = userRepository.findByUsername(username);
|
|
||||||
final Set<User> guests = user.getGuests();
|
|
||||||
// make sure we're broadcasting from host
|
|
||||||
for (final User guest : guests) {
|
|
||||||
// broadcast to endpoint the object device, with receiving user set to guest
|
|
||||||
endpoint.queueDeviceUpdate(device, guest, false, user.getId(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (causedByTrigger) {
|
|
||||||
endpoint.queueDeviceUpdate(device, user, false, user.getId(), false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -202,15 +116,12 @@ public class DeviceService {
|
||||||
*/
|
*/
|
||||||
public <T extends Device> List<T> saveAllAsOwner(
|
public <T extends Device> List<T> saveAllAsOwner(
|
||||||
Iterable<T> devices, String username, boolean fromScene, boolean fromTrigger) {
|
Iterable<T> devices, String username, boolean fromScene, boolean fromTrigger) {
|
||||||
devices.forEach(d -> renameIfDuplicate(d, username));
|
List<T> toReturn =
|
||||||
devices = deviceRepository.saveAll(devices);
|
devicePropagationService.saveAllAsOwner(devices, username, fromScene, fromTrigger);
|
||||||
devices.forEach((d) -> propagateUpdateAsOwner(d, username, fromScene && fromTrigger));
|
|
||||||
|
|
||||||
if (!fromScene) {
|
if (!fromScene) {
|
||||||
devices.forEach((d) -> triggerTriggers(d, username));
|
toReturn.forEach((d) -> this.triggerTriggers(d, username));
|
||||||
}
|
}
|
||||||
|
return toReturn;
|
||||||
return toList(devices);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends Device> List<T> saveAllAsOwner(Iterable<T> devices, String username) {
|
public <T extends Device> List<T> saveAllAsOwner(Iterable<T> devices, String username) {
|
||||||
|
@ -218,29 +129,51 @@ public class DeviceService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends Device> T saveAsOwner(T device, String username) {
|
public <T extends Device> T saveAsOwner(T device, String username) {
|
||||||
renameIfDuplicate(device, username);
|
T toReturn = devicePropagationService.saveAsOwner(device, username);
|
||||||
device = deviceRepository.save(device);
|
triggerTriggers(toReturn, username);
|
||||||
propagateUpdateAsOwner(device, username, false);
|
|
||||||
|
|
||||||
triggerTriggers(device, username);
|
|
||||||
|
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteByIdAsOwner(Long id, String username) throws NotFoundException {
|
public List<Device> findAll(Long roomId, Long hostId, String username)
|
||||||
Device d =
|
throws NotFoundException {
|
||||||
deviceRepository
|
Iterable<Device> devices;
|
||||||
.findByIdAndUsername(id, username)
|
User host = null;
|
||||||
|
if (hostId == null) {
|
||||||
|
if (roomId != null) {
|
||||||
|
roomRepository
|
||||||
|
.findByIdAndUsername(roomId, username)
|
||||||
.orElseThrow(NotFoundException::new);
|
.orElseThrow(NotFoundException::new);
|
||||||
|
devices = deviceRepository.findByRoomId(roomId);
|
||||||
|
} else {
|
||||||
|
devices = deviceRepository.findAllByUsername(username);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final User guest = userRepository.findByUsername(username);
|
||||||
|
host = userRepository.findById(hostId).orElseThrow(NotFoundException::new);
|
||||||
|
|
||||||
final User user = userRepository.findByUsername(username);
|
if (!guest.getHosts().contains(host)) {
|
||||||
final Set<User> guests = user.getGuests();
|
throw new NotFoundException();
|
||||||
// make sure we're broadcasting from host
|
}
|
||||||
for (final User guest : guests) {
|
|
||||||
// broadcast to endpoint the object device, with receiving user set to guest
|
if (roomId != null) {
|
||||||
endpoint.queueDeviceUpdate(d, guest, false, user.getId(), true);
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceRepository.delete(d);
|
devicePopulationService.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,30 +10,31 @@ import org.springframework.stereotype.Component;
|
||||||
public class SceneService {
|
public class SceneService {
|
||||||
|
|
||||||
@Autowired private DeviceRepository<Device> deviceRepository;
|
@Autowired private DeviceRepository<Device> deviceRepository;
|
||||||
@Autowired private DeviceService deviceService;
|
@Autowired private DevicePopulationService devicePopulationService;
|
||||||
|
@Autowired private DevicePropagationService devicePropagationService;
|
||||||
@Autowired private StateRepository<State<?>> stateRepository;
|
@Autowired private StateRepository<State<?>> stateRepository;
|
||||||
|
|
||||||
private List<Device> copyStatesToDevices(Scene fromScene) {
|
private List<Device> copyStatesToDevices(Scene fromScene) {
|
||||||
final List<Device> updated = new ArrayList<>();
|
final List<Device> updated = new ArrayList<>(fromScene.getStates().size());
|
||||||
|
|
||||||
for (final State<?> s : fromScene.getStates()) {
|
for (final State<?> s : fromScene.getStates()) {
|
||||||
s.apply();
|
s.apply();
|
||||||
updated.add(s.getDevice());
|
updated.add(s.getDevice());
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceService.populateComputedFields(updated);
|
devicePopulationService.populateComputedFields(updated);
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Device> apply(Scene newScene, String username, boolean fromTrigger) {
|
public List<Device> apply(Scene newScene, String username, boolean fromTrigger) {
|
||||||
List<Device> updated = copyStatesToDevices(newScene);
|
List<Device> updated = copyStatesToDevices(newScene);
|
||||||
deviceService.saveAllAsOwner(updated, username, true, fromTrigger);
|
devicePropagationService.saveAllAsOwner(updated, username, true, fromTrigger);
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Device> applyAsGuest(Scene newScene, String username, Long hostId) {
|
public List<Device> applyAsGuest(Scene newScene, String username, Long hostId) {
|
||||||
List<Device> updated = copyStatesToDevices(newScene);
|
List<Device> updated = copyStatesToDevices(newScene);
|
||||||
deviceService.saveAllAsGuestSceneApplication(updated, username, hostId);
|
devicePropagationService.saveAllAsGuestSceneApplication(updated, username, hostId);
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ public class ThermostatService {
|
||||||
|
|
||||||
@Autowired private SensorSocketEndpoint endpoint;
|
@Autowired private SensorSocketEndpoint endpoint;
|
||||||
|
|
||||||
@Autowired private DeviceService deviceService;
|
@Autowired private DevicePropagationService deviceService;
|
||||||
|
|
||||||
@Autowired private ThermostatRepository thermostatRepository;
|
@Autowired private ThermostatRepository thermostatRepository;
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.config.JWTTokenUtils;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Device;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Device;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DevicePopulationService;
|
||||||
import com.google.common.collect.HashMultimap;
|
import com.google.common.collect.HashMultimap;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.common.collect.Multimaps;
|
import com.google.common.collect.Multimaps;
|
||||||
|
@ -20,15 +20,15 @@ import org.springframework.stereotype.Component;
|
||||||
@Component
|
@Component
|
||||||
public class SensorSocketEndpoint extends Endpoint {
|
public class SensorSocketEndpoint extends Endpoint {
|
||||||
|
|
||||||
private Gson gson = GsonConfig.socketGson();
|
private final Gson gson = GsonConfig.socketGson();
|
||||||
|
|
||||||
@Autowired private DeviceService deviceService;
|
@Autowired private DevicePopulationService deviceService;
|
||||||
|
|
||||||
private UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
private JWTTokenUtils jwtTokenUtils;
|
private final JWTTokenUtils jwtTokenUtils;
|
||||||
|
|
||||||
private Multimap<User, Session> authorizedClients =
|
private final Multimap<User, Session> authorizedClients =
|
||||||
Multimaps.synchronizedMultimap(HashMultimap.create());
|
Multimaps.synchronizedMultimap(HashMultimap.create());
|
||||||
|
|
||||||
// messages are now stored as strings as a "hack" to capture and clone the state of the device,
|
// messages are now stored as strings as a "hack" to capture and clone the state of the device,
|
||||||
|
|
|
@ -4,7 +4,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTRequest;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTRequest;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTResponse;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTResponse;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse;
|
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateRegistrationException;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateRegistrationException;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UnauthorizedException;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UnauthorizedException;
|
||||||
|
@ -42,9 +41,9 @@ public class AuthenticationTests extends SmartHutTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setUp() {
|
protected void setUp() {
|
||||||
final ResponseEntity<OkResponse> res =
|
final ResponseEntity<Object> res =
|
||||||
this.restTemplate.postForEntity(
|
this.restTemplate.postForEntity(
|
||||||
this.url("/register"), getDisabledUser(), OkResponse.class);
|
this.url("/register"), getDisabledUser(), Object.class);
|
||||||
assertThat(res.getStatusCode().equals(HttpStatus.OK));
|
assertThat(res.getStatusCode().equals(HttpStatus.OK));
|
||||||
|
|
||||||
registerTestUser(restTemplate, userRepository, tokenRepository);
|
registerTestUser(restTemplate, userRepository, tokenRepository);
|
||||||
|
@ -178,8 +177,8 @@ public class AuthenticationTests extends SmartHutTest {
|
||||||
request.setEmail("smarthut.sm@example.com");
|
request.setEmail("smarthut.sm@example.com");
|
||||||
request.setPassword("password");
|
request.setPassword("password");
|
||||||
|
|
||||||
final ResponseEntity<OkResponse> res =
|
final ResponseEntity<Object> res =
|
||||||
this.restTemplate.postForEntity(url("/register"), request, OkResponse.class);
|
this.restTemplate.postForEntity(url("/register"), request, Object.class);
|
||||||
assertThat(res.getStatusCode().equals(HttpStatus.OK));
|
assertThat(res.getStatusCode().equals(HttpStatus.OK));
|
||||||
assertThat(res.getBody() != null);
|
assertThat(res.getBody() != null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse;
|
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationToken;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationToken;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository;
|
||||||
|
@ -40,8 +39,8 @@ public abstract class SmartHutTest {
|
||||||
final TestRestTemplate restTemplate,
|
final TestRestTemplate restTemplate,
|
||||||
final UserRepository userRepository,
|
final UserRepository userRepository,
|
||||||
final ConfirmationTokenRepository tokenRepository) {
|
final ConfirmationTokenRepository tokenRepository) {
|
||||||
final ResponseEntity<OkResponse> res2 =
|
final ResponseEntity<Object> res2 =
|
||||||
restTemplate.postForEntity(this.url("/register"), enabledUser, OkResponse.class);
|
restTemplate.postForEntity(this.url("/register"), enabledUser, Object.class);
|
||||||
assertThat(res2.getStatusCode().equals(HttpStatus.OK));
|
assertThat(res2.getStatusCode().equals(HttpStatus.OK));
|
||||||
|
|
||||||
final User persistedEnabledUser = userRepository.findByUsername("enabled");
|
final User persistedEnabledUser = userRepository.findByUsername("enabled");
|
||||||
|
|
Loading…
Reference in a new issue