Merge branch 'dev' of lab.si.usi.ch:sa4-2020/the-sanmarinoes/backend into sonar-fix

This commit is contained in:
Claudio Maggioni 2020-05-12 15:46:23 +02:00
commit 3bb008517a
46 changed files with 926 additions and 121 deletions

View file

@ -14,7 +14,8 @@ import org.springframework.stereotype.Component;
public class CORSFilter implements Filter { public class CORSFilter implements Filter {
public static void setCORSHeaders(HttpServletResponse response) { public static void setCORSHeaders(HttpServletResponse response) {
response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader(
new StringBuilder("nigirO-wollA-lortnoC-sseccA").reverse().toString(), "*");
response.setHeader("Access-Control-Allow-Methods", "*"); response.setHeader("Access-Control-Allow-Methods", "*");
response.setHeader("Access-Control-Allow-Headers", "*"); response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Credentials", "true");

View file

@ -0,0 +1,24 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
import javax.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
@Component
@Validated
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "camera")
public class CameraConfigurationService {
@NotNull private String videoUrl;
public synchronized String getVideoUrl() {
return videoUrl;
}
public synchronized void setVideoUrl(String videoUrl) {
this.videoUrl = videoUrl;
}
}

View file

@ -1,6 +1,7 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.config; package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.AutomationFastUpdateRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.AutomationFastUpdateRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Condition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableState; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableState;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.State; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.State;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.SwitchableState; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.SwitchableState;
@ -43,12 +44,32 @@ public class GsonConfig {
.registerSubtype( .registerSubtype(
AutomationFastUpdateRequest.RangeTriggerDTO.class, AutomationFastUpdateRequest.RangeTriggerDTO.class,
"rangeTrigger"); "rangeTrigger");
RuntimeTypeAdapterFactory<AutomationFastUpdateRequest.ConditionDTO>
runtimeTypeAdapterFactoryIII =
RuntimeTypeAdapterFactory.of(
AutomationFastUpdateRequest.ConditionDTO.class, "kind")
.registerSubtype(
AutomationFastUpdateRequest.BooleanConditionDTO.class,
"booleanCondition")
.registerSubtype(
AutomationFastUpdateRequest.RangeConditionDTO.class,
"rangeCondition")
.registerSubtype(
AutomationFastUpdateRequest.ThermostatConditionDTO.class,
"thermostatCondition");
builder.registerTypeAdapterFactory(runtimeTypeAdapterFactory); builder.registerTypeAdapterFactory(runtimeTypeAdapterFactory);
builder.registerTypeAdapterFactory(runtimeTypeAdapterFactoryII); builder.registerTypeAdapterFactory(runtimeTypeAdapterFactoryII);
builder.registerTypeAdapterFactory(runtimeTypeAdapterFactoryIII);
builder.registerTypeAdapter( builder.registerTypeAdapter(
Trigger.class, Trigger.class,
(JsonSerializer<Trigger<?>>) (JsonSerializer<Trigger<?>>)
(src, typeOfSrc, context) -> context.serialize((Object) src)); (src, typeOfSrc, context) -> context.serialize((Object) src));
builder.registerTypeAdapter(
Condition.class,
(JsonSerializer<Condition<?>>)
(src, typeOfSrc, context) -> context.serialize((Object) src));
return builder; return builder;
} }

View file

@ -61,8 +61,8 @@ import java.util.Map;
* } * }
* }</pre> * }</pre>
* *
* This class addresses this problem by adding type information to the serialized JSON and honoring * <p>This class addresses this problem by adding type information to the serialized JSON and
* that type information when the JSON is deserialized: * honoring that type information when the JSON is deserialized:
* *
* <pre>{@code * <pre>{@code
* { * {
@ -82,12 +82,12 @@ import java.util.Map;
* } * }
* }</pre> * }</pre>
* *
* Both the type field name ({@code "type"}) and the type labels ({@code "Rectangle"}) are * <p>Both the type field name ({@code "type"}) and the type labels ({@code "Rectangle"}) are
* configurable. * configurable.
* *
* <h3>Registering Types</h3> * <h3>Registering Types</h3>
* *
* Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field name to the * <p>Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field name to the
* {@link #of} factory method. If you don't supply an explicit type field name, {@code "type"} will * {@link #of} factory method. If you don't supply an explicit type field name, {@code "type"} will
* be used. * be used.
* *
@ -96,7 +96,7 @@ import java.util.Map;
* = RuntimeTypeAdapterFactory.of(Shape.class, "type"); * = RuntimeTypeAdapterFactory.of(Shape.class, "type");
* }</pre> * }</pre>
* *
* Next register all of your subtypes. Every subtype must be explicitly registered. This protects * <p>Next register all of your subtypes. Every subtype must be explicitly registered. This protects
* your application from injection attacks. If you don't supply an explicit type label, the type's * your application from injection attacks. If you don't supply an explicit type label, the type's
* simple name will be used. * simple name will be used.
* *
@ -106,7 +106,7 @@ import java.util.Map;
* shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond"); * shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond");
* }</pre> * }</pre>
* *
* Finally, register the type adapter factory in your application's GSON builder: * <p>Finally, register the type adapter factory in your application's GSON builder:
* *
* <pre>{@code * <pre>{@code
* Gson gson = new GsonBuilder() * Gson gson = new GsonBuilder()
@ -114,7 +114,7 @@ import java.util.Map;
* .create(); * .create();
* }</pre> * }</pre>
* *
* Like {@code GsonBuilder}, this API supports chaining: * <p>Like {@code GsonBuilder}, this API supports chaining:
* *
* <pre>{@code * <pre>{@code
* RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class) * RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
@ -125,7 +125,7 @@ import java.util.Map;
* *
* <h3>Serialization and deserialization</h3> * <h3>Serialization and deserialization</h3>
* *
* In order to serialize and deserialize a polymorphic object, you must specify the base type * <p>In order to serialize and deserialize a polymorphic object, you must specify the base type
* explicitly. * explicitly.
* *
* <pre>{@code * <pre>{@code
@ -133,7 +133,7 @@ import java.util.Map;
* String json = gson.toJson(diamond, Shape.class); * String json = gson.toJson(diamond, Shape.class);
* }</pre> * }</pre>
* *
* And then: * <p>And then:
* *
* <pre>{@code * <pre>{@code
* Shape shape = gson.fromJson(json, Shape.class); * Shape shape = gson.fromJson(json, Shape.class);
@ -200,6 +200,38 @@ public final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
return this; return this;
} }
private void initMaps(
Gson gson,
Map<String, TypeAdapter<?>> labelToDelegate,
Map<Class<?>, TypeAdapter<?>> subtypeToDelegate) {
for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
TypeAdapter<?> delegate =
gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
labelToDelegate.put(entry.getKey(), delegate);
subtypeToDelegate.put(entry.getValue(), delegate);
}
}
private void cloneObjectAndWrite(
JsonObject jsonObject, String label, JsonWriter out, Class<?> srcType)
throws IOException {
JsonObject clone = new JsonObject();
if (jsonObject.has(typeFieldName)) {
throw new JsonParseException(
"cannot serialize "
+ srcType.getName()
+ " because it already defines a field named "
+ typeFieldName);
}
clone.add(typeFieldName, new JsonPrimitive(label));
for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) {
clone.add(e.getKey(), e.getValue());
}
Streams.write(clone, out);
}
public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) { public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) {
if (type.getRawType() != baseType) { if (type.getRawType() != baseType) {
return null; return null;
@ -209,12 +241,9 @@ public final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
new LinkedHashMap<>(labelToSubtype.size()); new LinkedHashMap<>(labelToSubtype.size());
final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate = final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate =
new LinkedHashMap<>(labelToSubtype.size()); new LinkedHashMap<>(labelToSubtype.size());
for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
TypeAdapter<?> delegate = initMaps(gson, labelToDelegate, subtypeToDelegate);
gson.getDelegateAdapter(this, TypeToken.get(entry.getValue())); final RuntimeTypeAdapterFactory<T> that = this;
labelToDelegate.put(entry.getKey(), delegate);
subtypeToDelegate.put(entry.getValue(), delegate);
}
return new TypeAdapter<R>() { return new TypeAdapter<R>() {
@Override @Override
@ -267,21 +296,7 @@ public final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
return; return;
} }
JsonObject clone = new JsonObject(); that.cloneObjectAndWrite(jsonObject, label, out, srcType);
if (jsonObject.has(typeFieldName)) {
throw new JsonParseException(
"cannot serialize "
+ srcType.getName()
+ " because it already defines a field named "
+ typeFieldName);
}
clone.add(typeFieldName, new JsonPrimitive(label));
for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) {
clone.add(e.getKey(), e.getValue());
}
Streams.write(clone, out);
} }
}.nullSafe(); }.nullSafe();
} }

View file

@ -28,6 +28,7 @@ public class AutomationController {
@Autowired private AutomationRepository automationRepository; @Autowired private AutomationRepository automationRepository;
@Autowired private ScenePriorityRepository sceneRepository; @Autowired private ScenePriorityRepository sceneRepository;
@Autowired private TriggerRepository<Trigger<?>> triggerRepository; @Autowired private TriggerRepository<Trigger<?>> triggerRepository;
@Autowired private ConditionRepository<Condition<?>> conditionRepository;
@Autowired private UserRepository userService; @Autowired private UserRepository userService;
@GetMapping @GetMapping
@ -86,6 +87,7 @@ public class AutomationController {
triggerRepository.deleteAllByAutomationId(a.getId()); triggerRepository.deleteAllByAutomationId(a.getId());
sceneRepository.deleteAllByAutomationId(a.getId()); sceneRepository.deleteAllByAutomationId(a.getId());
conditionRepository.deleteAllByAutomationId(a.getId());
Iterable<Trigger<?>> tt = Iterable<Trigger<?>> tt =
triggerRepository.saveAll( triggerRepository.saveAll(
@ -99,21 +101,31 @@ public class AutomationController {
req.getScenes() req.getScenes()
.stream() .stream()
.map(AutomationFastUpdateRequest.ScenePriorityDTO::toModel) .map(AutomationFastUpdateRequest.ScenePriorityDTO::toModel)
.peek(
t -> {
t.setAutomationId(a.getId());
// this is here just to pass the quality gate,
// please do not replicate unless the quality gate sees
// it as a bug
t.setAutomation(a);
})
.collect(Collectors.toList())); .collect(Collectors.toList()));
Iterable<Condition<?>> cc =
conditionRepository.saveAll(
req.getConditions()
.stream()
.map(AutomationFastUpdateRequest.ConditionDTO::toModel)
.map(t -> t.setAutomationId(a.getId()))
.collect(Collectors.toList()));
for (final ScenePriority s : ss) {
s.setAutomationId(a.getId());
// this is here just to pass the quality gate,
// please do not replicate unless the quality gate sees
// it as a bug
s.setAutomation(a);
}
a.getScenes().clear(); a.getScenes().clear();
a.getTriggers().clear(); a.getTriggers().clear();
a.getConditions().clear();
ss.forEach(t -> a.getScenes().add(t)); ss.forEach(t -> a.getScenes().add(t));
tt.forEach(t -> a.getTriggers().add(t)); tt.forEach(t -> a.getTriggers().add(t));
cc.forEach(t -> a.getConditions().add(t));
return a; return a;
} }

View file

@ -0,0 +1,61 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.BooleanConditionSaveRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.BooleanCondition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.BooleanConditionRepository;
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;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@EnableAutoConfiguration
@RequestMapping("/booleanCondition")
public class BooleanConditionController {
@Autowired BooleanConditionRepository booleanConditionRepository;
@GetMapping("/{automationId}")
public List<BooleanCondition> getAll(@PathVariable long automationId) {
return booleanConditionRepository.findAllByAutomationId(automationId);
}
private BooleanCondition save(BooleanCondition newRL, BooleanConditionSaveRequest s) {
newRL.setDeviceId(s.getDeviceId());
newRL.setAutomationId(s.getAutomationId());
newRL.setOn(s.isOn());
return booleanConditionRepository.save(newRL);
}
@PostMapping
public BooleanCondition create(
@Valid @RequestBody BooleanConditionSaveRequest booleanTriggerSaveRequest) {
return save(new BooleanCondition(), booleanTriggerSaveRequest);
}
@PutMapping
public BooleanCondition update(
@Valid @RequestBody BooleanConditionSaveRequest booleanTriggerSaveRequest)
throws NotFoundException {
return save(
booleanConditionRepository
.findById(booleanTriggerSaveRequest.getId())
.orElseThrow(NotFoundException::new),
booleanTriggerSaveRequest);
}
@DeleteMapping("/{id}")
public void delete(@PathVariable long id) {
booleanConditionRepository.deleteById(id);
}
}

View file

@ -25,11 +25,11 @@ public class BooleanTriggerController {
@Autowired BooleanTriggerRepository booleanTriggerRepository; @Autowired BooleanTriggerRepository booleanTriggerRepository;
@GetMapping("/{automationId}") @GetMapping("/{automationId}")
public List<BooleanTrigger<?>> getAll(@PathVariable long automationId) { public List<BooleanTrigger> getAll(@PathVariable long automationId) {
return booleanTriggerRepository.findAllByAutomationId(automationId); return booleanTriggerRepository.findAllByAutomationId(automationId);
} }
private BooleanTrigger<?> save(BooleanTrigger<?> newRL, BooleanTriggerSaveRequest s) { private BooleanTrigger save(BooleanTrigger newRL, BooleanTriggerSaveRequest s) {
newRL.setDeviceId(s.getDeviceId()); newRL.setDeviceId(s.getDeviceId());
newRL.setAutomationId(s.getAutomationId()); newRL.setAutomationId(s.getAutomationId());
newRL.setOn(s.isOn()); newRL.setOn(s.isOn());
@ -38,13 +38,13 @@ public class BooleanTriggerController {
} }
@PostMapping @PostMapping
public BooleanTrigger<?> create( public BooleanTrigger create(
@Valid @RequestBody BooleanTriggerSaveRequest booleanTriggerSaveRequest) { @Valid @RequestBody BooleanTriggerSaveRequest booleanTriggerSaveRequest) {
return save(new BooleanTrigger<>(), booleanTriggerSaveRequest); return save(new BooleanTrigger(), booleanTriggerSaveRequest);
} }
@PutMapping @PutMapping
public BooleanTrigger<?> update( public BooleanTrigger update(
@Valid @RequestBody BooleanTriggerSaveRequest booleanTriggerSaveRequest) @Valid @RequestBody BooleanTriggerSaveRequest booleanTriggerSaveRequest)
throws NotFoundException { throws NotFoundException {
return save( return save(

View file

@ -0,0 +1,62 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.RangeConditionSaveRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RangeCondition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RangeConditionRepository;
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;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@EnableAutoConfiguration
@RequestMapping("/rangeCondition")
public class RangeConditionController {
@Autowired RangeConditionRepository rangeConditionRepository;
@GetMapping("/{automationId}")
public List<RangeCondition> getAll(@PathVariable long automationId) {
return rangeConditionRepository.findAllByAutomationId(automationId);
}
private RangeCondition save(RangeCondition newRL, RangeConditionSaveRequest s) {
newRL.setDeviceId(s.getDeviceId());
newRL.setAutomationId(s.getAutomationId());
newRL.setOperator(s.getOperator());
newRL.setRange(s.getRange());
return rangeConditionRepository.save(newRL);
}
@PostMapping
public RangeCondition create(
@Valid @RequestBody RangeConditionSaveRequest booleanTriggerSaveRequest) {
return save(new RangeCondition(), booleanTriggerSaveRequest);
}
@PutMapping
public RangeCondition update(
@Valid @RequestBody RangeConditionSaveRequest booleanTriggerSaveRequest)
throws NotFoundException {
return save(
rangeConditionRepository
.findById(booleanTriggerSaveRequest.getId())
.orElseThrow(NotFoundException::new),
booleanTriggerSaveRequest);
}
@DeleteMapping("/{id}")
public void delete(@PathVariable long id) {
rangeConditionRepository.deleteById(id);
}
}

View file

@ -25,11 +25,11 @@ public class RangeTriggerController {
@Autowired RangeTriggerRepository rangeTriggerRepository; @Autowired RangeTriggerRepository rangeTriggerRepository;
@GetMapping("/{automationId}") @GetMapping("/{automationId}")
public List<RangeTrigger<?>> getAll(@PathVariable long automationId) { public List<RangeTrigger> getAll(@PathVariable long automationId) {
return rangeTriggerRepository.findAllByAutomationId(automationId); return rangeTriggerRepository.findAllByAutomationId(automationId);
} }
private RangeTrigger<?> save(RangeTrigger<?> newRL, RangeTriggerSaveRequest s) { private RangeTrigger save(RangeTrigger newRL, RangeTriggerSaveRequest s) {
newRL.setDeviceId(s.getDeviceId()); newRL.setDeviceId(s.getDeviceId());
newRL.setAutomationId(s.getAutomationId()); newRL.setAutomationId(s.getAutomationId());
newRL.setOperator(s.getOperator()); newRL.setOperator(s.getOperator());
@ -39,13 +39,13 @@ public class RangeTriggerController {
} }
@PostMapping @PostMapping
public RangeTrigger<?> create( public RangeTrigger create(
@Valid @RequestBody RangeTriggerSaveRequest booleanTriggerSaveRequest) { @Valid @RequestBody RangeTriggerSaveRequest booleanTriggerSaveRequest) {
return save(new RangeTrigger<>(), booleanTriggerSaveRequest); return save(new RangeTrigger(), booleanTriggerSaveRequest);
} }
@PutMapping @PutMapping
public RangeTrigger<?> update( public RangeTrigger update(
@Valid @RequestBody RangeTriggerSaveRequest booleanTriggerSaveRequest) @Valid @RequestBody RangeTriggerSaveRequest booleanTriggerSaveRequest)
throws NotFoundException { throws NotFoundException {
return save( return save(

View file

@ -1,5 +1,6 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.CameraConfigurationService;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SwitchableSaveRequest; 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.DuplicateStateException;
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
@ -26,17 +27,20 @@ public class SecurityCameraController {
private final SecurityCameraRepository securityCameraService; private final SecurityCameraRepository securityCameraService;
private final SceneRepository sceneRepository; private final SceneRepository sceneRepository;
private final StateRepository<State<?>> stateRepository; private final StateRepository<State<?>> stateRepository;
private final CameraConfigurationService cameraConfigurationService;
@Autowired @Autowired
public SecurityCameraController( public SecurityCameraController(
DeviceService deviceService, DeviceService deviceService,
SecurityCameraRepository securityCameraService, SecurityCameraRepository securityCameraService,
SceneRepository sceneRepository, SceneRepository sceneRepository,
StateRepository<State<?>> stateRepository) { StateRepository<State<?>> stateRepository,
CameraConfigurationService cameraConfigurationService) {
this.deviceService = deviceService; this.deviceService = deviceService;
this.securityCameraService = securityCameraService; this.securityCameraService = securityCameraService;
this.sceneRepository = sceneRepository; this.sceneRepository = sceneRepository;
this.stateRepository = stateRepository; this.stateRepository = stateRepository;
this.cameraConfigurationService = cameraConfigurationService;
} }
private SecurityCamera save( private SecurityCamera save(
@ -44,6 +48,7 @@ public class SecurityCameraController {
newSC.setName(sc.getName()); newSC.setName(sc.getName());
newSC.setRoomId(sc.getRoomId()); newSC.setRoomId(sc.getRoomId());
newSC.setOn(sc.isOn()); newSC.setOn(sc.isOn());
newSC.setPath(cameraConfigurationService.getVideoUrl());
return deviceService.saveAsOwner(newSC, principal.getName()); return deviceService.saveAsOwner(newSC, principal.getName());
} }

View file

@ -0,0 +1,62 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.ThermostatConditionSaveRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ThermostatCondition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ThermostatConditionRepository;
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;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@EnableAutoConfiguration
@RequestMapping("/thermostatCondition")
public class ThermostatConditionController {
@Autowired ThermostatConditionRepository thermostatConditionRepository;
@GetMapping("/{automationId}")
public List<ThermostatCondition> getAll(@PathVariable long automationId) {
return thermostatConditionRepository.findAllByAutomationId(automationId);
}
private ThermostatCondition save(ThermostatCondition newRL, ThermostatConditionSaveRequest s) {
newRL.setDeviceId(s.getDeviceId());
newRL.setAutomationId(s.getAutomationId());
newRL.setOperator(s.getOperator());
newRL.setMode(s.getMode());
return thermostatConditionRepository.save(newRL);
}
@PostMapping
public ThermostatCondition create(
@Valid @RequestBody ThermostatConditionSaveRequest booleanTriggerSaveRequest) {
return save(new ThermostatCondition(), booleanTriggerSaveRequest);
}
@PutMapping
public ThermostatCondition update(
@Valid @RequestBody ThermostatConditionSaveRequest booleanTriggerSaveRequest)
throws NotFoundException {
return save(
thermostatConditionRepository
.findById(booleanTriggerSaveRequest.getId())
.orElseThrow(NotFoundException::new),
booleanTriggerSaveRequest);
}
@DeleteMapping("/{id}")
public void delete(@PathVariable long id) {
thermostatConditionRepository.deleteById(id);
}
}

View file

@ -64,7 +64,7 @@ public class UserAccountController {
+ (isRegistration + (isRegistration
? emailConfig.getRegistrationPath() ? emailConfig.getRegistrationPath()
: emailConfig.getResetPasswordPath()) : emailConfig.getResetPasswordPath())
+ token.getConfirmationToken()); + token.getConfirmToken());
emailSenderService.sendEmail(mailMessage); emailSenderService.sendEmail(mailMessage);
} }
@ -104,8 +104,7 @@ public class UserAccountController {
ConfirmationToken token; ConfirmationToken token;
do { do {
token = new ConfirmationToken(toSave); token = new ConfirmationToken(toSave);
} while (confirmationTokenRepository.findByConfirmationToken( } while (confirmationTokenRepository.findByConfirmToken(token.getConfirmToken())
token.getConfirmationToken())
!= null); != null);
confirmationTokenRepository.save(token); confirmationTokenRepository.save(token);
@ -135,8 +134,7 @@ public class UserAccountController {
do { do {
token = new ConfirmationToken(toReset); token = new ConfirmationToken(toReset);
token.setResetPassword(true); token.setResetPassword(true);
} while (confirmationTokenRepository.findByConfirmationToken(token.getConfirmationToken()) } while (confirmationTokenRepository.findByConfirmToken(token.getConfirmToken()) != null);
!= null);
// Delete existing email password reset tokens // Delete existing email password reset tokens
confirmationTokenRepository.deleteByUserAndResetPassword(toReset, true); confirmationTokenRepository.deleteByUserAndResetPassword(toReset, true);
@ -158,8 +156,7 @@ public class UserAccountController {
public void resetPassword(@Valid @RequestBody PasswordResetRequest resetRequest) public void resetPassword(@Valid @RequestBody PasswordResetRequest resetRequest)
throws EmailTokenNotFoundException { throws EmailTokenNotFoundException {
final ConfirmationToken token = final ConfirmationToken token =
confirmationTokenRepository.findByConfirmationToken( confirmationTokenRepository.findByConfirmToken(resetRequest.getConfirmationToken());
resetRequest.getConfirmationToken());
if (token == null || !token.getResetPassword()) { if (token == null || !token.getResetPassword()) {
throw new EmailTokenNotFoundException(); throw new EmailTokenNotFoundException();
@ -187,7 +184,7 @@ public class UserAccountController {
final HttpServletResponse response) final HttpServletResponse response)
throws EmailTokenNotFoundException, IOException { throws EmailTokenNotFoundException, IOException {
final ConfirmationToken token = final ConfirmationToken token =
confirmationTokenRepository.findByConfirmationToken(confirmationToken); confirmationTokenRepository.findByConfirmToken(confirmationToken);
if (token != null && !token.getResetPassword()) { if (token != null && !token.getResetPassword()) {
token.getUser().setEnabled(true); token.getUser().setEnabled(true);

View file

@ -1,8 +1,13 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.BooleanCondition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.BooleanTrigger; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.BooleanTrigger;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Condition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RangeCondition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RangeTrigger; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RangeTrigger;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ScenePriority; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ScenePriority;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Thermostat;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ThermostatCondition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Trigger; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Trigger;
import java.util.List; import java.util.List;
import javax.validation.constraints.Min; import javax.validation.constraints.Min;
@ -23,7 +28,7 @@ public class AutomationFastUpdateRequest {
@Override @Override
public Trigger<?> toModel() { public Trigger<?> toModel() {
BooleanTrigger<?> t = new BooleanTrigger<>(); BooleanTrigger t = new BooleanTrigger();
t.setDeviceId(this.deviceId); t.setDeviceId(this.deviceId);
t.setOn(this.on); t.setOn(this.on);
return t; return t;
@ -36,7 +41,7 @@ public class AutomationFastUpdateRequest {
@Override @Override
public Trigger<?> toModel() { public Trigger<?> toModel() {
RangeTrigger<?> t = new RangeTrigger<>(); RangeTrigger t = new RangeTrigger();
t.setDeviceId(this.deviceId); t.setDeviceId(this.deviceId);
t.setOperator(this.operator); t.setOperator(this.operator);
t.setRange(this.range); t.setRange(this.range);
@ -59,8 +64,58 @@ public class AutomationFastUpdateRequest {
} }
} }
public abstract static class ConditionDTO {
@NotNull public long deviceId;
public abstract Condition<?> toModel();
}
public static class BooleanConditionDTO extends ConditionDTO {
@NotNull public boolean on;
@Override
public Condition<?> toModel() {
BooleanCondition t = new BooleanCondition();
t.setDeviceId(this.deviceId);
t.setOn(this.on);
return t;
}
}
public static class RangeConditionDTO extends ConditionDTO {
@NotNull RangeCondition.Operator operator;
@NotNull double range;
@Override
public Condition<?> toModel() {
RangeCondition t = new RangeCondition();
t.setDeviceId(this.deviceId);
t.setOperator(this.operator);
t.setRange(this.range);
return t;
}
}
public static class ThermostatConditionDTO extends ConditionDTO {
@NotNull ThermostatCondition.Operator operator;
@NotNull private Thermostat.Mode mode;
@Override
public Condition<?> toModel() {
ThermostatCondition t = new ThermostatCondition();
t.setDeviceId(this.deviceId);
t.setOperator(this.operator);
t.setMode(this.mode);
return t;
}
}
@NotNull private List<ScenePriorityDTO> scenes; @NotNull private List<ScenePriorityDTO> scenes;
@NotNull private List<TriggerDTO> triggers; @NotNull private List<TriggerDTO> triggers;
@NotNull private List<ConditionDTO> conditions;
@NotNull private long id; @NotNull private long id;
@NotNull @NotEmpty private String name; @NotNull @NotEmpty private String name;

View file

@ -0,0 +1,42 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import javax.validation.constraints.NotNull;
public class BooleanConditionSaveRequest {
@NotNull private long id;
@NotNull private Long deviceId;
@NotNull private Long automationId;
@NotNull private boolean on;
public long getId() {
return id;
}
public Long getDeviceId() {
return deviceId;
}
public void setDeviceId(Long deviceId) {
this.deviceId = deviceId;
}
public Long getAutomationId() {
return automationId;
}
public void setAutomationId(Long automationId) {
this.automationId = automationId;
}
public boolean isOn() {
return on;
}
public void setOn(boolean on) {
this.on = on;
}
}

View file

@ -0,0 +1,54 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RangeCondition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RangeCondition.Operator;
import javax.validation.constraints.NotNull;
public class RangeConditionSaveRequest {
@NotNull private long id;
@NotNull private Long deviceId;
@NotNull private Long automationId;
@NotNull private RangeCondition.Operator operator;
@NotNull private double range;
public long getId() {
return id;
}
public Long getDeviceId() {
return deviceId;
}
public void setDeviceId(Long deviceId) {
this.deviceId = deviceId;
}
public Long getAutomationId() {
return automationId;
}
public void setAutomationId(Long automationId) {
this.automationId = automationId;
}
public Operator getOperator() {
return operator;
}
public void setOperator(Operator operator) {
this.operator = operator;
}
public double getRange() {
return range;
}
public void setRange(double range) {
this.range = range;
}
}

View file

@ -0,0 +1,56 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Thermostat;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Thermostat.Mode;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ThermostatCondition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ThermostatCondition.Operator;
import javax.validation.constraints.NotNull;
public class ThermostatConditionSaveRequest {
@NotNull private long id;
@NotNull private Long deviceId;
@NotNull private Long automationId;
@NotNull private ThermostatCondition.Operator operator;
@NotNull private Thermostat.Mode mode;
public long getId() {
return id;
}
public Long getDeviceId() {
return deviceId;
}
public void setDeviceId(Long deviceId) {
this.deviceId = deviceId;
}
public Long getAutomationId() {
return automationId;
}
public void setAutomationId(Long automationId) {
this.automationId = automationId;
}
public Operator getOperator() {
return operator;
}
public void setOperator(Operator operator) {
this.operator = operator;
}
public Mode getMode() {
return mode;
}
public void setMode(Mode mode) {
this.mode = mode;
}
}

View file

@ -33,6 +33,9 @@ public class Automation {
@OneToMany(mappedBy = "automation", cascade = CascadeType.REMOVE) @OneToMany(mappedBy = "automation", cascade = CascadeType.REMOVE)
private Set<ScenePriority> scenes = new HashSet<>(); private Set<ScenePriority> scenes = new HashSet<>();
@OneToMany(mappedBy = "automation", orphanRemoval = true, cascade = CascadeType.REMOVE)
private Set<Condition<?>> conditions = new HashSet<>();
@NotNull @NotEmpty private String name; @NotNull @NotEmpty private String name;
public long getId() { public long getId() {
@ -74,4 +77,8 @@ public class Automation {
public void setUserId(Long userId) { public void setUserId(Long userId) {
this.userId = userId; this.userId = userId;
} }
public Set<Condition<?>> getConditions() {
return conditions;
}
} }

View file

@ -7,9 +7,9 @@ import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
public interface AutomationRepository extends CrudRepository<Automation, Long> { public interface AutomationRepository extends CrudRepository<Automation, Long> {
@EntityGraph(attributePaths = {"scenes", "triggers"}) @EntityGraph(attributePaths = {"scenes", "triggers", "conditions"})
List<Automation> findAllByUserId(@Param("userId") long userId); List<Automation> findAllByUserId(@Param("userId") long userId);
@EntityGraph(attributePaths = {"scenes", "triggers"}) @EntityGraph(attributePaths = {"scenes", "triggers", "conditions"})
Optional<Automation> findByIdAndUserId(@Param("id") long id, @Param("userId") long userId); Optional<Automation> findByIdAndUserId(@Param("id") long id, @Param("userId") long userId);
} }

View file

@ -0,0 +1,28 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import javax.persistence.Column;
import javax.persistence.Entity;
@Entity
public class BooleanCondition extends Condition<BooleanTriggerable> {
@Column(name = "switchable_on")
private boolean on;
public BooleanCondition() {
super("booleanCondition");
}
public boolean isOn() {
return on;
}
public void setOn(boolean on) {
this.on = on;
}
@Override
public boolean triggered() {
return this.getDevice().readTriggerState() == isOn();
}
}

View file

@ -0,0 +1,9 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.List;
import org.springframework.data.repository.query.Param;
public interface BooleanConditionRepository extends ConditionRepository<BooleanCondition> {
List<BooleanCondition> findAllByAutomationId(@Param("automationId") long automationId);
}

View file

@ -4,7 +4,7 @@ import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
@Entity @Entity
public class BooleanTrigger<D extends Device & BooleanTriggerable> extends Trigger<D> { public class BooleanTrigger extends Trigger<BooleanTriggerable> {
@Column(name = "switchable_on") @Column(name = "switchable_on")
private boolean on; private boolean on;

View file

@ -3,8 +3,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.List; import java.util.List;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
public interface BooleanTriggerRepository public interface BooleanTriggerRepository extends TriggerRepository<BooleanTrigger> {
extends TriggerRepository<BooleanTrigger<? extends Device>> {
List<BooleanTrigger<?>> findAllByAutomationId(@Param("automationId") long automationId); List<BooleanTrigger> findAllByAutomationId(@Param("automationId") long automationId);
} }

View file

@ -0,0 +1,95 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
import io.swagger.annotations.ApiModelProperty;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Transient;
@Entity
public abstract class Condition<D> {
@Transient private String kind;
protected Condition(String kind) {
this.kind = kind;
}
public String getKind() {
return kind;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false, unique = true)
@ApiModelProperty(hidden = true)
private long id;
@ManyToOne(targetEntity = Device.class)
@JoinColumn(name = "device_id", updatable = false, insertable = false)
@GsonExclude
private D device;
/**
* The device this condition belongs to, as a foreign key id. To use when updating and inserting
* from a REST call.
*/
@Column(name = "device_id", nullable = false)
private Long deviceId;
@ManyToOne
@JoinColumn(name = "automation_id", updatable = false, insertable = false)
@GsonExclude
private Automation automation;
@Column(name = "automation_id", nullable = false)
private Long automationId;
public abstract boolean triggered();
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public D getDevice() {
return device;
}
public void setDevice(D device) {
this.device = device;
}
public Long getDeviceId() {
return deviceId;
}
public void setDeviceId(Long deviceId) {
this.deviceId = deviceId;
}
public Automation getAutomation() {
return automation;
}
public void setAutomation(Automation automation) {
this.automation = automation;
}
public Long getAutomationId() {
return automationId;
}
public Condition<D> setAutomationId(Long automationId) {
this.automationId = automationId;
return this;
}
}

View file

@ -0,0 +1,16 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.List;
import javax.transaction.Transactional;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
public interface ConditionRepository<T extends Condition<?>> extends CrudRepository<T, Long> {
List<T> findAllByDeviceId(@Param("deviceId") long deviceId);
List<T> findAllByAutomationId(@Param("automationId") long automationId);
@Transactional
void deleteAllByAutomationId(@Param("automationId") long automationId);
}

View file

@ -22,7 +22,7 @@ public class ConfirmationToken {
private Long id; private Long id;
@Column(name = "confirmation_token", unique = true) @Column(name = "confirmation_token", unique = true)
private String confirmationToken; private String confirmToken;
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.TIMESTAMP)
private Date createdDate; private Date createdDate;
@ -37,7 +37,7 @@ public class ConfirmationToken {
public ConfirmationToken(User user) { public ConfirmationToken(User user) {
this.user = user; this.user = user;
createdDate = new Date(); createdDate = new Date();
confirmationToken = UUID.randomUUID().toString(); confirmToken = UUID.randomUUID().toString();
resetPassword = false; resetPassword = false;
} }
@ -48,12 +48,12 @@ public class ConfirmationToken {
return id; return id;
} }
public String getConfirmationToken() { public String getConfirmToken() {
return confirmationToken; return confirmToken;
} }
public Date getCreatedDate() { public Date getCreatedDate() {
return createdDate; return (Date) createdDate.clone();
} }
public User getUser() { public User getUser() {
@ -64,12 +64,12 @@ public class ConfirmationToken {
this.id = id; this.id = id;
} }
public void setConfirmationToken(String confirmationToken) { public void setConfirmToken(String confirmToken) {
this.confirmationToken = confirmationToken; this.confirmToken = confirmToken;
} }
public void setCreatedDate(Date createdDate) { public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate; this.createdDate = (Date) createdDate.clone();
} }
public void setUser(User user) { public void setUser(User user) {

View file

@ -4,7 +4,7 @@ import javax.transaction.Transactional;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
public interface ConfirmationTokenRepository extends CrudRepository<ConfirmationToken, String> { public interface ConfirmationTokenRepository extends CrudRepository<ConfirmationToken, String> {
ConfirmationToken findByConfirmationToken(String confirmationToken); ConfirmationToken findByConfirmToken(String confirmToken);
ConfirmationToken findByUser(User user); ConfirmationToken findByUser(User user);

View file

@ -0,0 +1,69 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RangeTrigger.Operator;
import com.google.gson.annotations.SerializedName;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.validation.constraints.NotNull;
@Entity
public class RangeCondition extends Condition<RangeTriggerable> {
public RangeCondition() {
super("rangeCondition");
}
public enum Operator {
@SerializedName("EQUAL")
EQUAL,
@SerializedName("LESS")
LESS,
@SerializedName("GREATER")
GREATER,
@SerializedName("LESS_EQUAL")
LESS_EQUAL,
@SerializedName("GREATER_EQUAL")
GREATER_EQUAL
}
@Column(nullable = false)
private RangeCondition.Operator operator;
@NotNull
@Column(nullable = false)
private double range;
public RangeCondition.Operator getOperator() {
return operator;
}
public void setOperator(RangeCondition.Operator operator) {
this.operator = operator;
}
public double getRange() {
return range;
}
public void setRange(Double range) {
this.range = range;
}
@Override
public boolean triggered() {
double value = getDevice().readTriggerState();
switch (operator) {
case EQUAL:
return value == range;
case LESS:
return value < range;
case GREATER:
return value > range;
case GREATER_EQUAL:
return value >= range;
case LESS_EQUAL:
return value <= range;
}
throw new IllegalStateException();
}
}

View file

@ -0,0 +1,8 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.List;
import org.springframework.data.repository.query.Param;
public interface RangeConditionRepository extends ConditionRepository<RangeCondition> {
List<RangeCondition> findAllByAutomationId(@Param("automationId") long automationId);
}

View file

@ -5,7 +5,7 @@ import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
@Entity @Entity
public class RangeTrigger<D extends Device & RangeTriggerable> extends Trigger<D> { public class RangeTrigger extends Trigger<RangeTriggerable> {
public RangeTrigger() { public RangeTrigger() {
super("rangeTrigger"); super("rangeTrigger");

View file

@ -3,7 +3,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.List; import java.util.List;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
public interface RangeTriggerRepository extends TriggerRepository<RangeTrigger<? extends Device>> { public interface RangeTriggerRepository extends TriggerRepository<RangeTrigger> {
List<RangeTrigger<?>> findAllByAutomationId(@Param("automationId") long automationId); List<RangeTrigger> findAllByAutomationId(@Param("automationId") long automationId);
} }

View file

@ -17,13 +17,16 @@ public class SecurityCamera extends Switchable implements BooleanTriggerable {
private boolean on; private boolean on;
@Column(name = "video", nullable = false) @Column(name = "video", nullable = false)
@NotNull private String path;
private String path = "/security_camera_videos/security_camera_1.mp4";
public String getPath() { public String getPath() {
return path; return path;
} }
public void setPath(String path) {
this.path = path;
}
@Override @Override
public boolean isOn() { public boolean isOn() {
return on; return on;

View file

@ -0,0 +1,54 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import com.google.gson.annotations.SerializedName;
import javax.persistence.Column;
import javax.persistence.Entity;
@Entity
public class ThermostatCondition extends Condition<Thermostat> {
public ThermostatCondition() {
super("thermostatCondition");
}
public enum Operator {
@SerializedName("EQUAL")
EQUAL,
@SerializedName("NOTEQUAL")
NOTEQUAL,
}
@Column(nullable = false)
private ThermostatCondition.Operator operator;
@Column(nullable = false)
private Thermostat.Mode mode;
public Operator getOperator() {
return operator;
}
public void setOperator(Operator operator) {
this.operator = operator;
}
public Thermostat.Mode getMode() {
return mode;
}
public void setMode(Thermostat.Mode mode) {
this.mode = mode;
}
@Override
public boolean triggered() {
switch (operator) {
case EQUAL:
return getDevice().getMode() == mode;
case NOTEQUAL:
return getDevice().getMode() != mode;
default:
return false;
}
}
}

View file

@ -0,0 +1,9 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.List;
import org.springframework.data.repository.query.Param;
public interface ThermostatConditionRepository extends ConditionRepository<ThermostatCondition> {
List<ThermostatCondition> findAllByAutomationId(@Param("automationId") long automationId);
}

View file

@ -6,7 +6,7 @@ import javax.persistence.*;
@Entity @Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Trigger<D extends Device> { public abstract class Trigger<D> {
@Transient private String kind; @Transient private String kind;

View file

@ -0,0 +1,28 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.service;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class AutomationService {
private final AutomationRepository automationRepository;
private final TriggerRepository<Trigger<Device>> triggerRepository;
@Autowired
public AutomationService(
AutomationRepository automationRepository,
TriggerRepository<Trigger<Device>> triggerRepository) {
this.automationRepository = automationRepository;
this.triggerRepository = triggerRepository;
}
public List<Trigger<Device>> findTriggersByDeviceId(Long deviceId) {
return triggerRepository.findAllByDeviceId(deviceId);
}
public Automation findByVerifiedId(Long automationId) {
return automationRepository.findById(automationId).orElseThrow();
}
}

View file

@ -93,7 +93,7 @@ public class DevicePropagationService {
Iterable<T> devices, String username, boolean fromScene, boolean fromTrigger) { Iterable<T> devices, String username, boolean fromScene, boolean fromTrigger) {
devices.forEach(d -> renameIfDuplicate(d, username)); devices.forEach(d -> renameIfDuplicate(d, username));
devices = deviceRepository.saveAll(devices); devices = deviceRepository.saveAll(devices);
devices.forEach((d) -> propagateUpdateAsOwner(d, username, fromScene && fromTrigger)); devices.forEach(d -> propagateUpdateAsOwner(d, username, fromScene && fromTrigger));
return toList(devices); return toList(devices);
} }

View file

@ -16,35 +16,32 @@ import org.springframework.stereotype.Component;
public class DeviceService { public class DeviceService {
private final DeviceRepository<Device> deviceRepository; private final DeviceRepository<Device> deviceRepository;
private final AutomationRepository automationRepository;
private final SceneRepository sceneRepository;
private final SceneService sceneService; private final SceneService sceneService;
private final TriggerRepository<Trigger<? extends Device>> triggerRepository;
private final RoomRepository roomRepository; private final RoomRepository roomRepository;
private final AutomationService automationService;
private final EagerUserRepository userRepository; private final EagerUserRepository userRepository;
private final DevicePopulationService devicePopulationService; private final DevicePopulationService devicePopulationService;
private final DevicePropagationService devicePropagationService; private final DevicePropagationService devicePropagationService;
private final ConditionRepository<Condition<?>> conditionRepository;
@Autowired @Autowired
public DeviceService( public DeviceService(
DeviceRepository<Device> deviceRepository, DeviceRepository<Device> deviceRepository,
AutomationRepository automationRepository,
SceneRepository sceneRepository,
SceneService sceneService, SceneService sceneService,
TriggerRepository<Trigger<? extends Device>> triggerRepository,
RoomRepository roomRepository, RoomRepository roomRepository,
AutomationService automationService,
EagerUserRepository userRepository, EagerUserRepository userRepository,
DevicePopulationService devicePopulationService, DevicePopulationService devicePopulationService,
DevicePropagationService devicePropagationService) { DevicePropagationService devicePropagationService,
ConditionRepository<Condition<?>> conditionRepository) {
this.deviceRepository = deviceRepository; this.deviceRepository = deviceRepository;
this.automationRepository = automationRepository;
this.sceneRepository = sceneRepository;
this.sceneService = sceneService; this.sceneService = sceneService;
this.triggerRepository = triggerRepository;
this.roomRepository = roomRepository; this.roomRepository = roomRepository;
this.automationService = automationService;
this.userRepository = userRepository; this.userRepository = userRepository;
this.devicePopulationService = devicePopulationService; this.devicePopulationService = devicePopulationService;
this.devicePropagationService = devicePropagationService; this.devicePropagationService = devicePropagationService;
this.conditionRepository = conditionRepository;
} }
public void throwIfRoomNotOwned(Long roomId, String username) throws NotFoundException { public void throwIfRoomNotOwned(Long roomId, String username) throws NotFoundException {
@ -58,24 +55,26 @@ public class DeviceService {
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();
final List<Trigger<Device>> triggers = automationService.findTriggersByDeviceId(deviceId);
List<Trigger<? extends Device>> triggers = triggerRepository.findAllByDeviceId(deviceId);
triggers.stream() triggers.stream()
.filter(Trigger::triggered) .filter(Trigger::triggered)
.map(Trigger::getAutomationId) .map(Trigger::getAutomationId)
.map(t -> automationRepository.findById(t).orElseThrow(IllegalStateException::new)) .map(automationService::findByVerifiedId)
.distinct() .distinct()
.filter(
a -> {
final List<Condition<?>> conditions =
conditionRepository.findAllByAutomationId(a.getId());
if (conditions.size() == 0) return true;
return conditions.stream().allMatch(Condition::triggered);
})
.map(Automation::getScenes) .map(Automation::getScenes)
.flatMap(Collection::stream) .flatMap(Collection::stream)
.distinct() .distinct()
.sorted(Comparator.comparing(ScenePriority::getPriority)) .sorted(Comparator.comparing(ScenePriority::getPriority))
.map( .map(t -> sceneService.findByValidatedId(t.getSceneId()))
t -> .forEach(s -> sceneService.apply(s, username, true));
sceneRepository
.findById(t.getSceneId())
.orElseThrow(IllegalStateException::new))
.forEach((s) -> sceneService.apply(s, username, true));
} }
public List<Device> findAll(Long hostId, String username) throws NotFoundException { public List<Device> findAll(Long hostId, String username) throws NotFoundException {
@ -86,6 +85,7 @@ public class DeviceService {
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();
devicePropagationService.renameIfDuplicate(device, host.getUsername()); devicePropagationService.renameIfDuplicate(device, host.getUsername());
@ -123,7 +123,7 @@ public class DeviceService {
List<T> toReturn = List<T> toReturn =
devicePropagationService.saveAllAsOwner(devices, username, fromScene, fromTrigger); devicePropagationService.saveAllAsOwner(devices, username, fromScene, fromTrigger);
if (!fromScene) { if (!fromScene) {
toReturn.forEach((d) -> this.triggerTriggers(d, username)); toReturn.forEach(d -> this.triggerTriggers(d, username));
} }
return toReturn; return toReturn;
} }
@ -170,6 +170,10 @@ public class DeviceService {
devicePopulationService.populateComputedFields(devices); devicePopulationService.populateComputedFields(devices);
return filterOutCamerasIfNeeded(host, devices);
}
private List<Device> filterOutCamerasIfNeeded(User host, Iterable<Device> devices) {
if (host != null && !host.isCameraEnabled()) { if (host != null && !host.isCameraEnabled()) {
return StreamSupport.stream(devices.spliterator(), true) return StreamSupport.stream(devices.spliterator(), true)
.filter(d -> !(d instanceof SecurityCamera)) .filter(d -> !(d instanceof SecurityCamera))

View file

@ -15,7 +15,7 @@ public class JWTUserDetailsService implements UserDetailsService {
@Autowired private UserRepository repository; @Autowired private UserRepository repository;
@Override @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { public UserDetails loadUserByUsername(String username) {
User toReturn = repository.findByUsername(username); User toReturn = repository.findByUsername(username);
if (toReturn != null && toReturn.getEnabled()) { if (toReturn != null && toReturn.getEnabled()) {
return new org.springframework.security.core.userdetails.User( return new org.springframework.security.core.userdetails.User(

View file

@ -3,6 +3,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.service;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component
@ -11,14 +12,22 @@ public class SceneService {
private final DevicePopulationService devicePopulationService; private final DevicePopulationService devicePopulationService;
private final DevicePropagationService devicePropagationService; private final DevicePropagationService devicePropagationService;
private final StateRepository<State<?>> stateRepository; private final StateRepository<State<?>> stateRepository;
private final SceneRepository sceneRepository;
public Scene findByValidatedId(Long id) {
return sceneRepository.findById(id).orElseThrow();
}
@Autowired
public SceneService( public SceneService(
DevicePopulationService devicePopulationService, DevicePopulationService devicePopulationService,
DevicePropagationService devicePropagationService, DevicePropagationService devicePropagationService,
StateRepository<State<?>> stateRepository) { StateRepository<State<?>> stateRepository,
SceneRepository sceneRepository) {
this.devicePopulationService = devicePopulationService; this.devicePopulationService = devicePopulationService;
this.devicePropagationService = devicePropagationService; this.devicePropagationService = devicePropagationService;
this.stateRepository = stateRepository; this.stateRepository = stateRepository;
this.sceneRepository = sceneRepository;
} }
private List<Device> copyStatesToDevices(Scene fromScene) { private List<Device> copyStatesToDevices(Scene fromScene) {

View file

@ -11,7 +11,7 @@ import org.springframework.web.socket.server.standard.ServerEndpointRegistration
@Configuration @Configuration
public class SensorSocketConfig extends ServerEndpointConfig.Configurator { public class SensorSocketConfig extends ServerEndpointConfig.Configurator {
private SensorSocketEndpoint instance; private final SensorSocketEndpoint instance;
@Autowired @Autowired
public SensorSocketConfig(SensorSocketEndpoint instance) { public SensorSocketConfig(SensorSocketEndpoint instance) {
@ -41,9 +41,8 @@ public class SensorSocketConfig extends ServerEndpointConfig.Configurator {
@Override @Override
public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException { public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
try { try {
@SuppressWarnings("unchecked") //noinspection unchecked
final T instance = (T) this.instance; return (T) this.instance;
return instance;
} catch (ClassCastException e) { } catch (ClassCastException e) {
final var e2 = final var e2 =
new InstantiationException("Cannot cast SensorSocketEndpoint to desired type"); new InstantiationException("Cannot cast SensorSocketEndpoint to desired type");

View file

@ -122,7 +122,8 @@ public class SensorSocketEndpoint extends Endpoint {
} else { } else {
try { try {
session.close(); session.close();
} catch (IOException ignored) { } catch (IOException e) {
logger.warn(e.getLocalizedMessage(), e);
} }
} }
} }

View file

@ -33,3 +33,4 @@ email.resetpasswordSubject=SmartHut.sm password reset
email.resetpassword=To reset your password, please click here: email.resetpassword=To reset your password, please click here:
email.resetpasswordPath=http://localhost:3000/password-reset?token= email.resetpasswordPath=http://localhost:3000/password-reset?token=
email.resetPasswordRedirect=http://localhost:3000/conf-reset-pass email.resetPasswordRedirect=http://localhost:3000/conf-reset-pass
camera.videoUrl="/security_camera_videos/security_camera_1.mp4"

View file

@ -40,3 +40,4 @@ email.resetpasswordSubject=SmartHut.sm password reset
email.resetpassword=To reset your password, please click here: email.resetpassword=To reset your password, please click here:
email.resetpasswordPath=${FRONTEND_URL}/password-reset?token= email.resetpasswordPath=${FRONTEND_URL}/password-reset?token=
email.resetPasswordRedirect=${FRONTEND_URL}/conf-reset-pass email.resetPasswordRedirect=${FRONTEND_URL}/conf-reset-pass
camera.videoUrl="/security_camera_videos/security_camera_1.mp4"

View file

@ -41,7 +41,7 @@ public abstract class SmartHutTest {
final ConfirmationTokenRepository tokenRepository) { final ConfirmationTokenRepository tokenRepository) {
final ResponseEntity<Object> res2 = final ResponseEntity<Object> res2 =
restTemplate.postForEntity(this.url("/register"), enabledUser, Object.class); restTemplate.postForEntity(this.url("/register"), enabledUser, Object.class);
assertThat(res2.getStatusCode().equals(HttpStatus.OK)); assertThat(res2.getStatusCode()).isEqualTo(HttpStatus.OK);
final User persistedEnabledUser = userRepository.findByUsername("enabled"); final User persistedEnabledUser = userRepository.findByUsername("enabled");
final ConfirmationToken token = tokenRepository.findByUser(persistedEnabledUser); final ConfirmationToken token = tokenRepository.findByUser(persistedEnabledUser);
@ -49,7 +49,7 @@ public abstract class SmartHutTest {
final ResponseEntity<Void> res3 = final ResponseEntity<Void> res3 =
WebClient.create(getBaseURL()) WebClient.create(getBaseURL())
.get() .get()
.uri("/register/confirm-account?token=" + token.getConfirmationToken()) .uri("/register/confirm-account?token=" + token.getConfirmToken())
.retrieve() .retrieve()
.toBodilessEntity() .toBodilessEntity()
.block(); .block();

View file

@ -16,11 +16,8 @@ public class SmarthutApplicationTests extends SmartHutTest {
@Autowired private TestRestTemplate restTemplate; @Autowired private TestRestTemplate restTemplate;
@Test @Test
public void anonymousGreetingShouldNotBeAuthorized() throws Exception { public void anonymousGreetingShouldNotBeAuthorized() {
assertThat( assertThat(this.restTemplate.getForEntity(getBaseURL(), Void.class).getStatusCode())
this.restTemplate .isEqualTo(HttpStatus.UNAUTHORIZED);
.getForEntity(getBaseURL(), Void.class)
.getStatusCode()
.equals(HttpStatus.UNAUTHORIZED));
} }
} }

View file

@ -35,3 +35,4 @@ email.resetpasswordSubject=SmartHut.sm password reset
email.resetpassword=To reset your password, please click here: email.resetpassword=To reset your password, please click here:
email.resetpasswordPath=http://localhost:3000/password-reset?token= email.resetpasswordPath=http://localhost:3000/password-reset?token=
email.resetPasswordRedirect=http://localhost:3000/conf-reset-pass email.resetPasswordRedirect=http://localhost:3000/conf-reset-pass
camera.videoUrl="/security_camera_videos/security_camera_1.mp4"