Merge branch 'dev' of lab.si.usi.ch:sa4-2020/the-sanmarinoes/backend into 51-users-can-add-smart-curtains-blinds
# Conflicts: # .gitlab-ci.yml
This commit is contained in:
commit
7f34e2b391
49 changed files with 572 additions and 198 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,5 +1,7 @@
|
|||
.idea/**
|
||||
|
||||
data/**/*
|
||||
|
||||
**/.DS_Store
|
||||
|
||||
# Compiled class file
|
||||
|
@ -138,4 +140,4 @@ gradle-app.setting
|
|||
# gradle/wrapper/gradle-wrapper.properties
|
||||
|
||||
# IntelliJ
|
||||
*.iml
|
||||
*.iml
|
||||
|
|
|
@ -62,4 +62,4 @@ code_quality:
|
|||
paths:
|
||||
- build/reports/cpd/cpdCheck.xml
|
||||
#create a report on the quality of the code
|
||||
expose_as: 'Code Quality Report'
|
||||
expose_as: 'Code Quality Report'
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
FROM openjdk:13-jdk-alpine
|
||||
ENV DB_URL ""
|
||||
ENV DB_USER ""
|
||||
ENV DB_PASS ""
|
||||
|
||||
ARG JAR_FILE=build/libs/smarthut*.jar
|
||||
|
||||
COPY ${JAR_FILE} app.jar
|
||||
|
||||
ENV SMARTHUT_THIS_VALUE_IS_PROD_IF_THIS_IS_A_CONTAINER_PIZZOCCHERI=prod
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["java","-jar","/app.jar"]
|
||||
|
|
13
HELP.md
13
HELP.md
|
@ -1,13 +0,0 @@
|
|||
# Getting Started
|
||||
|
||||
### Reference Documentation
|
||||
For further reference, please consider the following sections:
|
||||
|
||||
* [Official Gradle documentation](https://docs.gradle.org)
|
||||
* [Spring Boot Gradle Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.2.4.RELEASE/gradle-plugin/reference/html/)
|
||||
|
||||
### Additional Links
|
||||
These additional references should also help you:
|
||||
|
||||
* [Gradle Build Scans – insights for your project's build](https://scans.gradle.com#gradle)
|
||||
|
51
README.md
51
README.md
|
@ -1,2 +1,53 @@
|
|||
# backend
|
||||
|
||||
## Installation guide
|
||||
|
||||
In order to install a SmartHut.sm, you can use *Docker* and *Docker Compose*
|
||||
in order to create che corresponding containers.
|
||||
|
||||
Use the following `docker-compose.yml` example file. Change the values
|
||||
of `$PASSWORD` and `$SECRET` to respectively the chosen PostgreSQL password
|
||||
and the JWT secret used to run the server. `$SECRET` must be at least 64 chars long.
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
smarthutdb:
|
||||
restart: always
|
||||
image: postgres:12-alpine
|
||||
container_name: smarthutdb
|
||||
volumes:
|
||||
- ./data:/var/lib/postgresql/data
|
||||
environment:
|
||||
PGDATA: /var/lib/postgresql/data/data
|
||||
POSTGRES_DB: smarthut
|
||||
POSTGRES_USERNAME: postgres
|
||||
POSTGRES_PASSWORD: $PASSWORD
|
||||
|
||||
smarthutbackend:
|
||||
restart: always
|
||||
image: smarthutsm/smarthut-backend:M1
|
||||
ports:
|
||||
- 8080:8080
|
||||
environment:
|
||||
- POSTGRES_JDBC=jdbc:postgresql://smarthutdb:5432/smarthut
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASS=$PASSWORD
|
||||
- SECRET=$SECRET
|
||||
- MAIL_HOST=smtp.gmail.com
|
||||
- MAIL_PORT=587
|
||||
- MAIL_STARTTLS=true
|
||||
- MAIL_USER=smarthut.sm@gmail.com
|
||||
- MAIL_PASS=dcadvbagqfkwbfts
|
||||
- BACKEND_URL=http://localhost:8080
|
||||
- FRONTEND_URL=http://localhost
|
||||
|
||||
smarthut:
|
||||
restart: always
|
||||
image: smarthutsm/smarthut:M1
|
||||
ports:
|
||||
- 80:80
|
||||
environment:
|
||||
- BACKEND_URL=http://localhost:8080
|
||||
```
|
||||
|
|
13
build.gradle
13
build.gradle
|
@ -4,21 +4,16 @@ plugins {
|
|||
id "de.aaschmid.cpd" version "3.1"
|
||||
id 'java'
|
||||
}
|
||||
|
||||
group = 'ch.usi.inf.sa4.sanmarinoes'
|
||||
version = '0.0.1-SNAPSHOT'
|
||||
sourceCompatibility = '11'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final'
|
||||
compile "org.springframework.boot:spring-boot-starter-websocket"
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter'
|
||||
|
||||
implementation 'com.sun.mail:javax.mail:1.6.2'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
|
@ -32,22 +27,18 @@ dependencies {
|
|||
compile 'io.springfox:springfox-swagger2:2.9.2'
|
||||
compile 'io.springfox:springfox-swagger-ui:2.9.2'
|
||||
compile 'org.springframework.boot:spring-boot-configuration-processor'
|
||||
|
||||
testCompile 'org.springframework.boot:spring-boot-starter-webflux'
|
||||
implementation('org.springframework.boot:spring-boot-starter-web') {
|
||||
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-json'
|
||||
}
|
||||
|
||||
testImplementation('org.springframework.boot:spring-boot-starter-test') {
|
||||
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
|
||||
}
|
||||
|
||||
testImplementation 'org.springframework.security:spring-security-test'
|
||||
testImplementation 'com.h2database:h2:1.4.200'
|
||||
|
||||
// Fixes https://stackoverflow.com/a/60455550
|
||||
testImplementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.11'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
@ -14,23 +13,13 @@ import org.springframework.stereotype.Component;
|
|||
@Component
|
||||
public class CORSFilter implements Filter {
|
||||
|
||||
static void setCORSHeaders(HttpServletResponse response) {
|
||||
public static void setCORSHeaders(HttpServletResponse response) {
|
||||
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||
response.setHeader("Access-Control-Allow-Methods", "HEAD, PUT, POST, GET, OPTIONS, DELETE");
|
||||
response.setHeader("Access-Control-Max-Age", "3600");
|
||||
response.setHeader(
|
||||
"Access-Control-Allow-Headers",
|
||||
String.join(
|
||||
",",
|
||||
List.of(
|
||||
"Access-Control-Allow-Headers",
|
||||
"Origin",
|
||||
"Accept",
|
||||
"X-Requested-With",
|
||||
"Authorization",
|
||||
"Content-Type",
|
||||
"Access-Control-Request-Method",
|
||||
"Access-Control-Request-Headers")));
|
||||
response.setHeader("Access-Control-Allow-Methods", "*");
|
||||
response.setHeader("Access-Control-Allow-Headers", "*");
|
||||
response.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
response.setHeader("Access-Control-Expose-Headers", "*");
|
||||
response.setHeader("Access-Control-Max-Age", "6".repeat(99));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -30,17 +30,21 @@ public class EmailConfigurationService {
|
|||
*/
|
||||
@NotNull private String registrationPath;
|
||||
|
||||
/**
|
||||
* The URL to follow for password reset email confirmation. Has to end with the start of a query
|
||||
* parameter
|
||||
*/
|
||||
@NotNull private String resetPasswordPath;
|
||||
|
||||
/** The email subject for a reset password email */
|
||||
@NotNull private String resetPasswordSubject;
|
||||
|
||||
/** The text in the email body preceding the confirmation URL for a reset password email */
|
||||
@NotNull private String resetPassword;
|
||||
|
||||
/**
|
||||
* The URL to follow for password reset email confirmation. Has to end with the start of a query
|
||||
* parameter
|
||||
*/
|
||||
@NotNull private String resetPasswordPath;
|
||||
@NotNull private String resetPasswordRedirect;
|
||||
|
||||
@NotNull private String registrationRedirect;
|
||||
|
||||
public String getRegistrationSubject() {
|
||||
return registrationSubject;
|
||||
|
@ -89,4 +93,20 @@ public class EmailConfigurationService {
|
|||
public void setResetPasswordPath(String resetPasswordPath) {
|
||||
this.resetPasswordPath = resetPasswordPath;
|
||||
}
|
||||
|
||||
public String getResetPasswordRedirect() {
|
||||
return resetPasswordRedirect;
|
||||
}
|
||||
|
||||
public void setResetPasswordRedirect(String resetPasswordRedirect) {
|
||||
this.resetPasswordRedirect = resetPasswordRedirect;
|
||||
}
|
||||
|
||||
public String getRegistrationRedirect() {
|
||||
return registrationRedirect;
|
||||
}
|
||||
|
||||
public void setRegistrationRedirect(String registrationRedirect) {
|
||||
this.registrationRedirect = registrationRedirect;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -75,7 +74,8 @@ public class SpringFoxConfig {
|
|||
.or(PathSelectors.regex("/sensor.*")::apply)
|
||||
.or(PathSelectors.regex("/smartPlug.*")::apply)
|
||||
.or(PathSelectors.regex("/switch.*")::apply)
|
||||
.or(PathSelectors.regex("/motionSensor.*")::apply);
|
||||
.or(PathSelectors.regex("/motionSensor.*")::apply)
|
||||
.or(PathSelectors.regex("/auth/profile.*")::apply);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,11 +3,9 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
|||
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.JWTTokenUtils;
|
||||
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.UserUpdateRequest;
|
||||
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.models.*;
|
||||
import io.swagger.annotations.Authorization;
|
||||
import java.security.Principal;
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
|
@ -72,19 +70,9 @@ public class AuthenticationController {
|
|||
return new JWTResponse(token);
|
||||
}
|
||||
|
||||
@Authorization(value = "Bearer")
|
||||
@PatchMapping("/update")
|
||||
public User update(
|
||||
@Valid @RequestBody final UserUpdateRequest userData, final Principal principal) {
|
||||
final User oldUser = userRepository.findByUsername(principal.getName());
|
||||
if (userData.getName() != null) oldUser.setName(userData.getName());
|
||||
if (userData.getEmail() != null) {
|
||||
oldUser.setEmail(userData.getEmail());
|
||||
// TODO: handle email verification
|
||||
}
|
||||
if (userData.getPassword() != null)
|
||||
oldUser.setPassword(encoder.encode(userData.getPassword()));
|
||||
return userRepository.save(oldUser);
|
||||
@GetMapping("/profile")
|
||||
public User profile(final Principal principal) {
|
||||
return userRepository.findByUsername(principal.getName());
|
||||
}
|
||||
|
||||
private void authenticate(String username, String password) throws UnauthorizedException {
|
||||
|
|
|
@ -6,6 +6,7 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.ButtonDimmerDimRequest;
|
|||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.validation.Valid;
|
||||
|
@ -52,10 +53,13 @@ public class ButtonDimmerController
|
|||
}
|
||||
|
||||
@PutMapping("/dim")
|
||||
public Set<DimmableLight> dim(@Valid @RequestBody final ButtonDimmerDimRequest bd)
|
||||
public Set<DimmableLight> dim(
|
||||
@Valid @RequestBody final ButtonDimmerDimRequest bd, final Principal principal)
|
||||
throws NotFoundException {
|
||||
final ButtonDimmer buttonDimmer =
|
||||
buttonDimmerRepository.findById(bd.getId()).orElseThrow(NotFoundException::new);
|
||||
buttonDimmerRepository
|
||||
.findByIdAndUsername(bd.getId(), principal.getName())
|
||||
.orElseThrow(NotFoundException::new);
|
||||
|
||||
switch (bd.getDimType()) {
|
||||
case UP:
|
||||
|
|
|
@ -27,16 +27,17 @@ public class DeviceController {
|
|||
}
|
||||
|
||||
@PutMapping
|
||||
public Device update(@Valid @RequestBody DeviceSaveRequest deviceSaveRequest)
|
||||
public Device update(
|
||||
@Valid @RequestBody DeviceSaveRequest deviceSaveRequest, final Principal principal)
|
||||
throws NotFoundException, BadDataException {
|
||||
final Device d =
|
||||
deviceRepository
|
||||
.findById(deviceSaveRequest.getId())
|
||||
.findByIdAndUsername(deviceSaveRequest.getId(), principal.getName())
|
||||
.orElseThrow(NotFoundException::new);
|
||||
|
||||
// check if roomId is valid
|
||||
roomRepository
|
||||
.findById(deviceSaveRequest.getRoomId())
|
||||
.findByIdAndUsername(deviceSaveRequest.getRoomId(), principal.getName())
|
||||
.orElseThrow(() -> new BadDataException("roomId is not a valid room id"));
|
||||
|
||||
d.setRoomId(deviceSaveRequest.getRoomId());
|
||||
|
|
|
@ -6,6 +6,7 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.DimmableLightSaveRequest;
|
|||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableLight;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableLightRepository;
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -43,10 +44,14 @@ public class DimmableLightController {
|
|||
}
|
||||
|
||||
@PutMapping
|
||||
public DimmableLight update(@Valid @RequestBody DimmableLightSaveRequest sp)
|
||||
public DimmableLight update(
|
||||
@Valid @RequestBody DimmableLightSaveRequest sp, final Principal principal)
|
||||
throws NotFoundException {
|
||||
return save(
|
||||
dimmableLightService.findById(sp.getId()).orElseThrow(NotFoundException::new), sp);
|
||||
dimmableLightService
|
||||
.findByIdAndUsername(sp.getId(), principal.getName())
|
||||
.orElseThrow(NotFoundException::new),
|
||||
sp);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
|
|
|
@ -6,6 +6,7 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest;
|
|||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.KnobDimmerDimRequest;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.validation.Valid;
|
||||
|
@ -53,10 +54,13 @@ public class KnobDimmerController
|
|||
}
|
||||
|
||||
@PutMapping("/dimTo")
|
||||
public Set<DimmableLight> dimTo(@Valid @RequestBody final KnobDimmerDimRequest bd)
|
||||
public Set<DimmableLight> dimTo(
|
||||
@Valid @RequestBody final KnobDimmerDimRequest bd, final Principal principal)
|
||||
throws NotFoundException {
|
||||
final KnobDimmer dimmer =
|
||||
knobDimmerRepository.findById(bd.getId()).orElseThrow(NotFoundException::new);
|
||||
knobDimmerRepository
|
||||
.findByIdAndUsername(bd.getId(), principal.getName())
|
||||
.orElseThrow(NotFoundException::new);
|
||||
|
||||
dimmer.setLightIntensity(bd.getIntensity());
|
||||
dimmableLightRepository.saveAll(dimmer.getOutputs());
|
||||
|
|
|
@ -6,6 +6,7 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.RegularLightSaveRequest;
|
|||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RegularLight;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RegularLightRepository;
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -50,10 +51,14 @@ public class RegularLightController {
|
|||
}
|
||||
|
||||
@PutMapping
|
||||
public RegularLight update(@Valid @RequestBody RegularLightSaveRequest rl)
|
||||
public RegularLight update(
|
||||
@Valid @RequestBody RegularLightSaveRequest rl, final Principal principal)
|
||||
throws NotFoundException {
|
||||
return save(
|
||||
regularLightService.findById(rl.getId()).orElseThrow(NotFoundException::new), rl);
|
||||
regularLightService
|
||||
.findByIdAndUsername(rl.getId(), principal.getName())
|
||||
.orElseThrow(NotFoundException::new),
|
||||
rl);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
|
|
|
@ -23,6 +23,12 @@ public class RoomController {
|
|||
|
||||
@Autowired private DeviceRepository<Device> deviceRepository;
|
||||
|
||||
@Autowired private SwitchRepository switchRepository;
|
||||
|
||||
@Autowired private ButtonDimmerRepository buttonDimmerRepository;
|
||||
|
||||
@Autowired private KnobDimmerRepository knobDimmerRepository;
|
||||
|
||||
@GetMapping
|
||||
public List<Room> findAll() {
|
||||
return toList(roomRepository.findAll());
|
||||
|
@ -33,44 +39,57 @@ public class RoomController {
|
|||
return roomRepository.findById(id).orElseThrow(NotFoundException::new);
|
||||
}
|
||||
|
||||
private Room save(final RoomSaveRequest r, final Principal principal, boolean setWhenNull) {
|
||||
Room newRoom = new Room();
|
||||
@PostMapping
|
||||
public @ResponseBody Room create(
|
||||
@Valid @RequestBody RoomSaveRequest r, final Principal principal) {
|
||||
|
||||
final String username = principal.getName();
|
||||
final Long userId = userRepository.findByUsername(username).getId();
|
||||
final String img = r.getImage();
|
||||
final Room.Icon icon = r.getIcon();
|
||||
|
||||
final Room newRoom = new Room();
|
||||
newRoom.setUserId(userId);
|
||||
newRoom.setName(r.getName());
|
||||
if (img != null) {
|
||||
newRoom.setImage(img);
|
||||
} else if (setWhenNull) {
|
||||
newRoom.setImage(null);
|
||||
newRoom.setImage(img);
|
||||
newRoom.setIcon(icon);
|
||||
|
||||
return roomRepository.save(newRoom);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public @ResponseBody Room update(
|
||||
@PathVariable("id") long id, @RequestBody RoomSaveRequest r, final Principal principal)
|
||||
throws NotFoundException {
|
||||
final Room newRoom =
|
||||
roomRepository
|
||||
.findByIdAndUsername(id, principal.getName())
|
||||
.orElseThrow(NotFoundException::new);
|
||||
final String img = r.getImage();
|
||||
final Room.Icon icon = r.getIcon();
|
||||
|
||||
if (r.getName() != null) {
|
||||
newRoom.setName(r.getName());
|
||||
}
|
||||
|
||||
if ("".equals(img)) {
|
||||
newRoom.setImage(null);
|
||||
} else if (img != null) {
|
||||
newRoom.setImage(img);
|
||||
}
|
||||
|
||||
if (icon != null) {
|
||||
newRoom.setIcon(icon);
|
||||
} else if (setWhenNull) {
|
||||
newRoom.setIcon(null);
|
||||
}
|
||||
|
||||
return roomRepository.save(newRoom);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public @ResponseBody Room create(
|
||||
@Valid @RequestBody RoomSaveRequest r, final Principal principal) {
|
||||
return this.save(r, principal, true);
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
public @ResponseBody Room update(
|
||||
@Valid @RequestBody RoomSaveRequest r, final Principal principal) {
|
||||
return this.save(r, principal, false);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public void deleteById(@PathVariable("id") long id) {
|
||||
switchRepository.deleteAllByRoomId(id);
|
||||
knobDimmerRepository.deleteAllByRoomId(id);
|
||||
buttonDimmerRepository.deleteAllByRoomId(id);
|
||||
roomRepository.deleteById(id);
|
||||
}
|
||||
|
||||
|
@ -79,7 +98,7 @@ public class RoomController {
|
|||
* id).
|
||||
*/
|
||||
@GetMapping(path = "/{roomId}/devices")
|
||||
public @ResponseBody List<? extends Device> getDevices(@PathVariable("roomId") long roomid) {
|
||||
public List<Device> getDevices(@PathVariable("roomId") long roomid) {
|
||||
return deviceRepository.findByRoomId(roomid);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
|
|||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SmartPlugSaveRequest;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||
import java.security.Principal;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
|
@ -14,7 +15,7 @@ import org.springframework.web.bind.annotation.*;
|
|||
|
||||
@RestController
|
||||
@EnableAutoConfiguration
|
||||
@RequestMapping("/smartplug")
|
||||
@RequestMapping("/smartPlug")
|
||||
public class SmartPlugController {
|
||||
|
||||
@Autowired private SmartPlugRepository smartPlugRepository;
|
||||
|
@ -44,9 +45,25 @@ public class SmartPlugController {
|
|||
}
|
||||
|
||||
@PutMapping
|
||||
public SmartPlug update(@Valid @RequestBody SmartPlugSaveRequest sp) throws NotFoundException {
|
||||
public SmartPlug update(@Valid @RequestBody SmartPlugSaveRequest sp, final Principal principal)
|
||||
throws NotFoundException {
|
||||
return save(
|
||||
smartPlugRepository.findById(sp.getId()).orElseThrow(NotFoundException::new), sp);
|
||||
smartPlugRepository
|
||||
.findByIdAndUsername(sp.getId(), principal.getName())
|
||||
.orElseThrow(NotFoundException::new),
|
||||
sp);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}/meter")
|
||||
public SmartPlug resetMeter(@PathVariable("id") long id, final Principal principal)
|
||||
throws NotFoundException {
|
||||
final SmartPlug s =
|
||||
smartPlugRepository
|
||||
.findByIdAndUsername(id, principal.getName())
|
||||
.orElseThrow(NotFoundException::new);
|
||||
|
||||
s.resetTotalConsumption();
|
||||
return smartPlugRepository.save(s);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
|
|
|
@ -6,6 +6,7 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest;
|
|||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SwitchOperationRequest;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||
import java.security.Principal;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
|
@ -55,9 +56,13 @@ public class SwitchController extends InputDeviceConnectionController<Switch, Sw
|
|||
}
|
||||
|
||||
@PutMapping("/operate")
|
||||
public Set<Switchable> operate(@Valid @RequestBody final SwitchOperationRequest sr)
|
||||
public Set<Switchable> operate(
|
||||
@Valid @RequestBody final SwitchOperationRequest sr, final Principal principal)
|
||||
throws NotFoundException {
|
||||
final Switch s = switchRepository.findById(sr.getId()).orElseThrow(NotFoundException::new);
|
||||
final Switch s =
|
||||
switchRepository
|
||||
.findByIdAndUsername(sr.getId(), principal.getName())
|
||||
.orElseThrow(NotFoundException::new);
|
||||
|
||||
switch (sr.getType()) {
|
||||
case ON:
|
||||
|
|
|
@ -13,6 +13,8 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository;
|
|||
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.service.EmailSenderService;
|
||||
import java.io.IOException;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
|
@ -158,8 +160,10 @@ public class UserAccountController {
|
|||
* @throws EmailTokenNotFoundException if given token is not a valid token for password reset
|
||||
*/
|
||||
@PutMapping("/reset-password")
|
||||
public OkResponse resetPassword(@Valid @RequestBody PasswordResetRequest resetRequest)
|
||||
throws EmailTokenNotFoundException {
|
||||
public OkResponse resetPassword(
|
||||
@Valid @RequestBody PasswordResetRequest resetRequest,
|
||||
final HttpServletResponse response)
|
||||
throws EmailTokenNotFoundException, IOException {
|
||||
final ConfirmationToken token =
|
||||
confirmationTokenRepository.findByConfirmationToken(
|
||||
resetRequest.getConfirmationToken());
|
||||
|
@ -187,16 +191,17 @@ public class UserAccountController {
|
|||
* confirmation
|
||||
*/
|
||||
@GetMapping(value = "/confirm-account")
|
||||
public OkResponse confirmUserAccount(@RequestParam("token") @NotNull String confirmationToken)
|
||||
throws EmailTokenNotFoundException {
|
||||
public void confirmUserAccount(
|
||||
@RequestParam("token") @NotNull String confirmationToken,
|
||||
final HttpServletResponse response)
|
||||
throws EmailTokenNotFoundException, IOException {
|
||||
final ConfirmationToken token =
|
||||
confirmationTokenRepository.findByConfirmationToken(confirmationToken);
|
||||
|
||||
if (token != null && !token.getResetPassword()) {
|
||||
token.getUser().setEnabled(true);
|
||||
userRepository.save(token.getUser());
|
||||
// TODO: redirect to frontend
|
||||
return new OkResponse();
|
||||
response.sendRedirect(emailConfig.getRegistrationRedirect());
|
||||
} else {
|
||||
throw new EmailTokenNotFoundException();
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ public class DimmableLightSaveRequest {
|
|||
|
||||
/** The light intensity value. Goes from 0 (off) to 100 (on) */
|
||||
@NotNull
|
||||
@Min(1)
|
||||
@Min(0)
|
||||
@Max(100)
|
||||
private Integer intensity = 0;
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@ import javax.validation.constraints.NotNull;
|
|||
|
||||
public class RoomSaveRequest {
|
||||
|
||||
/** Room identifier */
|
||||
private long id;
|
||||
|
||||
@NotNull private Room.Icon icon;
|
||||
|
||||
/**
|
||||
|
@ -19,6 +22,14 @@ public class RoomSaveRequest {
|
|||
/** The user given name of this room (e.g. 'Master bedroom') */
|
||||
@NotNull private String name;
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ public class ButtonDimmer extends Dimmer {
|
|||
private static final int DIM_INCREMENT = 10;
|
||||
|
||||
public ButtonDimmer() {
|
||||
super("button-dimmer");
|
||||
super("buttonDimmer");
|
||||
}
|
||||
|
||||
/** Increases the current intensity level of the dimmable light by DIM_INCREMENT */
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||
|
||||
public interface ButtonDimmerRepository extends DeviceRepository<ButtonDimmer> {}
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
public interface ButtonDimmerRepository extends DeviceRepository<ButtonDimmer> {
|
||||
|
||||
@Transactional
|
||||
void deleteAllByRoomId(long roomId);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import org.springframework.data.repository.CrudRepository;
|
|||
public interface ConfirmationTokenRepository extends CrudRepository<ConfirmationToken, String> {
|
||||
ConfirmationToken findByConfirmationToken(String confirmationToken);
|
||||
|
||||
ConfirmationToken findByUser(User user);
|
||||
|
||||
@Transactional
|
||||
void deleteByUserAndResetPassword(User user, boolean resetPassword);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public abstract class Device {
|
|||
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
|
||||
* a REST call.
|
||||
*/
|
||||
@Column(name = "room_id", nullable = false, unique = true)
|
||||
@Column(name = "room_id", nullable = false)
|
||||
@NotNull
|
||||
private Long roomId;
|
||||
|
||||
|
@ -49,17 +49,13 @@ public abstract class Device {
|
|||
* The name for the category of this particular device (e.g 'dimmer'). Not stored in the
|
||||
* database but set thanks to constructors
|
||||
*/
|
||||
@ApiModelProperty(hidden = true)
|
||||
@Transient
|
||||
private final String kind;
|
||||
@Transient private final String kind;
|
||||
|
||||
/**
|
||||
* The way this device behaves in the automation flow. Not stored in the database but set thanks
|
||||
* to constructors
|
||||
*/
|
||||
@ApiModelProperty(hidden = true)
|
||||
@Transient
|
||||
private final FlowType flowType;
|
||||
@Transient private final FlowType flowType;
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
|
|
|
@ -2,6 +2,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.transaction.Transactional;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
@ -20,6 +21,7 @@ public interface DeviceRepository<T extends Device> extends CrudRepository<T, Lo
|
|||
* @param username a User's username
|
||||
* @return an optional device, empty if none found
|
||||
*/
|
||||
@Transactional
|
||||
@Query("SELECT d FROM Device d JOIN d.room r JOIN r.user u WHERE d.id = ?1 AND u.username = ?2")
|
||||
Optional<T> findByIdAndUsername(Long id, String username);
|
||||
|
||||
|
@ -29,6 +31,7 @@ public interface DeviceRepository<T extends Device> extends CrudRepository<T, Lo
|
|||
* @param username the User's username
|
||||
* @return all devices of that user
|
||||
*/
|
||||
@Transactional
|
||||
@Query("SELECT d FROM Device d JOIN d.room r JOIN r.user u WHERE u.username = ?1")
|
||||
List<T> findAllByUsername(String username);
|
||||
|
||||
|
@ -38,6 +41,7 @@ public interface DeviceRepository<T extends Device> extends CrudRepository<T, Lo
|
|||
* @param deviceId the device id
|
||||
* @return a user object
|
||||
*/
|
||||
@Transactional
|
||||
@Query("SELECT u FROM Device d JOIN d.room r JOIN r.user u WHERE d.id = ?1")
|
||||
User findUser(Long deviceId);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ public class DimmableLight extends Switchable {
|
|||
Connector.basic(KnobDimmer::getOutputs, DimmableLight::setDimmerId);
|
||||
|
||||
public DimmableLight() {
|
||||
super("light");
|
||||
super("dimmableLight");
|
||||
}
|
||||
|
||||
@ManyToOne
|
||||
|
@ -39,6 +39,10 @@ public class DimmableLight extends Switchable {
|
|||
@Max(100)
|
||||
private Integer intensity = 0;
|
||||
|
||||
@NotNull
|
||||
@Column(nullable = false)
|
||||
private Integer oldIntensity = 100;
|
||||
|
||||
public Integer getIntensity() {
|
||||
return intensity;
|
||||
}
|
||||
|
@ -55,8 +59,10 @@ public class DimmableLight extends Switchable {
|
|||
this.intensity = 0;
|
||||
} else if (intensity > 100) {
|
||||
this.intensity = 100;
|
||||
this.oldIntensity = 100;
|
||||
} else {
|
||||
this.intensity = intensity;
|
||||
this.oldIntensity = intensity;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,13 +73,13 @@ public class DimmableLight extends Switchable {
|
|||
|
||||
@Override
|
||||
public void setOn(boolean on) {
|
||||
intensity = on ? 100 : 0;
|
||||
intensity = on ? oldIntensity : 0;
|
||||
}
|
||||
|
||||
public void setDimmerId(Long dimmerId) {
|
||||
this.dimmerId = dimmerId;
|
||||
super.setSwitchId(null);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSwitchId(Long switchId) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import javax.persistence.Entity;
|
|||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.PreRemove;
|
||||
|
||||
/** Represents a generic dimmer input device */
|
||||
@Entity
|
||||
|
@ -32,4 +33,11 @@ public abstract class Dimmer extends InputDevice {
|
|||
public void addDimmableLight(DimmableLight dimmableLight) {
|
||||
lights.add(dimmableLight);
|
||||
}
|
||||
|
||||
@PreRemove
|
||||
private void removeLightsFromDimmer() {
|
||||
for (DimmableLight dl : getOutputs()) {
|
||||
dl.setDimmerId(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import javax.persistence.Entity;
|
|||
public class KnobDimmer extends Dimmer {
|
||||
|
||||
public KnobDimmer() {
|
||||
super("knob-dimmer");
|
||||
super("knobDimmer");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||
|
||||
public interface KnobDimmerRepository extends DeviceRepository<KnobDimmer> {}
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
public interface KnobDimmerRepository extends DeviceRepository<KnobDimmer> {
|
||||
|
||||
@Transactional
|
||||
void deleteAllByRoomId(long roomId);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,6 @@ public class MotionSensor extends InputDevice {
|
|||
}
|
||||
|
||||
public MotionSensor() {
|
||||
super("motion-sensor");
|
||||
super("motionSensor");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ public class RegularLight extends Switchable {
|
|||
boolean on;
|
||||
|
||||
public RegularLight() {
|
||||
super("regular-light");
|
||||
super("regularLight");
|
||||
this.on = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
|||
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
|
@ -125,7 +127,6 @@ public class Room {
|
|||
* https://www.baeldung.com/java-base64-image-string
|
||||
* https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html
|
||||
*/
|
||||
@Lob
|
||||
@Column(name = "image", columnDefinition = "TEXT")
|
||||
private String image;
|
||||
|
||||
|
@ -134,12 +135,16 @@ public class Room {
|
|||
@GsonExclude
|
||||
private User user;
|
||||
|
||||
@OneToMany(mappedBy = "room", orphanRemoval = true)
|
||||
@GsonExclude
|
||||
private Set<Device> devices = new HashSet<>();
|
||||
|
||||
/**
|
||||
* User that owns the house this room is in as a foreign key id. To use when updating and
|
||||
* inserting from a REST call.
|
||||
*/
|
||||
@NotNull
|
||||
@Column(name = "user_id", nullable = false, unique = true)
|
||||
@Column(name = "user_id", nullable = false)
|
||||
private Long userId;
|
||||
|
||||
/** The user given name of this room (e.g. 'Master bedroom') */
|
||||
|
@ -187,6 +192,10 @@ public class Room {
|
|||
this.image = image;
|
||||
}
|
||||
|
||||
public Set<Device> getDevices() {
|
||||
return devices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Room{" + "id=" + id + ", name='" + name + "\'}";
|
||||
|
|
|
@ -1,5 +1,18 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
public interface RoomRepository extends CrudRepository<Room, Long> {}
|
||||
public interface RoomRepository extends CrudRepository<Room, Long> {
|
||||
|
||||
/**
|
||||
* Finds a room by their id and a username
|
||||
*
|
||||
* @param id the room id
|
||||
* @param username a User's username
|
||||
* @return an optional device, empty if none found
|
||||
*/
|
||||
@Query("SELECT r FROM Room r JOIN r.user u WHERE r.id = ?1 AND u.username = ?2")
|
||||
Optional<Room> findByIdAndUsername(Long id, String username);
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ public class Sensor extends InputDevice {
|
|||
}
|
||||
|
||||
/** The value of this sensor according to its sensor type */
|
||||
@Column(nullable = false, length = 10, precision = 1)
|
||||
@Column(nullable = false, precision = 11, scale = 1)
|
||||
private BigDecimal value;
|
||||
|
||||
/** The type of this sensor */
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
@ -8,11 +9,28 @@ import javax.validation.constraints.NotNull;
|
|||
@Entity
|
||||
public class SmartPlug extends Switchable {
|
||||
|
||||
/** The average consumption of an active plug when on in Watt */
|
||||
public static final Double AVERAGE_CONSUMPTION_KW = 200.0;
|
||||
|
||||
/** The total amount of power that the smart plug has consumed represented in W/h */
|
||||
@Column(precision = 13, scale = 3)
|
||||
@NotNull
|
||||
private BigDecimal totalConsumption = BigDecimal.ZERO;
|
||||
|
||||
/** Whether the smart plug is on */
|
||||
@Column(name = "smart_plug_on", nullable = false)
|
||||
@NotNull
|
||||
private boolean on;
|
||||
|
||||
public BigDecimal getTotalConsumption() {
|
||||
return totalConsumption;
|
||||
}
|
||||
|
||||
/** Resets the consuption meter */
|
||||
public void resetTotalConsumption() {
|
||||
totalConsumption = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOn() {
|
||||
return on;
|
||||
|
@ -24,6 +42,6 @@ public class SmartPlug extends Switchable {
|
|||
}
|
||||
|
||||
public SmartPlug() {
|
||||
super("smart-plug");
|
||||
super("smartPlug");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,24 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||
|
||||
public interface SmartPlugRepository extends SwitchableRepository<SmartPlug> {}
|
||||
import java.util.Collection;
|
||||
import javax.transaction.Transactional;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
|
||||
public interface SmartPlugRepository extends SwitchableRepository<SmartPlug> {
|
||||
@Transactional
|
||||
Collection<SmartPlug> findByOn(boolean on);
|
||||
|
||||
/**
|
||||
* Updates total consumption of all activated smart plugs by considering a load of
|
||||
* fakeConsumption W. This query must be executed every second
|
||||
*
|
||||
* @see ch.usi.inf.sa4.sanmarinoes.smarthut.scheduled.UpdateTasks
|
||||
* @param fakeConsumption the fake consumption in watts
|
||||
*/
|
||||
@Modifying(clearAutomatically = true)
|
||||
@Transactional
|
||||
@Query(
|
||||
"UPDATE SmartPlug s SET totalConsumption = s.totalConsumption + ?1 / 3600.0 WHERE s.on = true")
|
||||
void updateTotalConsumption(Double fakeConsumption);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import java.util.Set;
|
|||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.PreRemove;
|
||||
|
||||
/** A switch input device */
|
||||
@Entity
|
||||
|
@ -51,4 +52,11 @@ public class Switch extends InputDevice {
|
|||
public Set<Switchable> getOutputs() {
|
||||
return switchables;
|
||||
}
|
||||
|
||||
@PreRemove
|
||||
public void removeSwitchable() {
|
||||
for (Switchable s : getOutputs()) {
|
||||
s.setSwitchId(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||
|
||||
public interface SwitchRepository extends DeviceRepository<Switch> {}
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
public interface SwitchRepository extends DeviceRepository<Switch> {
|
||||
|
||||
@Transactional
|
||||
void deleteAllByRoomId(long roomId);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import java.util.Objects;
|
||||
import javax.persistence.*;
|
||||
|
@ -24,6 +25,7 @@ public class User {
|
|||
|
||||
/** A properly salted way to store the password */
|
||||
@Column(nullable = false)
|
||||
@GsonExclude
|
||||
private String password;
|
||||
|
||||
/**
|
||||
|
@ -34,7 +36,7 @@ public class User {
|
|||
private String email;
|
||||
|
||||
@Column(nullable = false)
|
||||
@ApiModelProperty(hidden = true)
|
||||
@GsonExclude
|
||||
private Boolean isEnabled = false;
|
||||
|
||||
public Long getId() {
|
||||
|
|
|
@ -2,10 +2,10 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.scheduled;
|
|||
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.controller.MotionSensorController;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.controller.SensorController;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.MotionSensorRepository;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Sensor;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.SensorRepository;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
@ -13,18 +13,25 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/** Generates fake sensor (and motion sensor) updates as required by milestone one */
|
||||
/**
|
||||
* Generates fake sensor (and motion sensor) and smart plug consumption updates as required by
|
||||
* milestone one
|
||||
*/
|
||||
@Component
|
||||
public class SensorUpdateTasks {
|
||||
public class UpdateTasks {
|
||||
|
||||
@Autowired private SensorRepository sensorRepository;
|
||||
|
||||
@Autowired private MotionSensorRepository motionSensorRepository;
|
||||
|
||||
@Autowired private SmartPlugRepository smartPlugRepository;
|
||||
|
||||
@Autowired private SensorController sensorController;
|
||||
|
||||
@Autowired private MotionSensorController motionSensorController;
|
||||
|
||||
@Autowired private SensorSocketEndpoint sensorSocketEndpoint;
|
||||
|
||||
/** Generates fake sensor updates every two seconds with a +/- 1.25% error */
|
||||
@Scheduled(fixedRate = 2000)
|
||||
public void sensorFakeUpdate() {
|
||||
|
@ -59,4 +66,12 @@ public class SensorUpdateTasks {
|
|||
sensor, false));
|
||||
});
|
||||
}
|
||||
|
||||
/** Updates power consumption of all activated smart plugs every second */
|
||||
@Scheduled(fixedDelay = 1000)
|
||||
public void smartPlugConsumptionFakeUpdate() {
|
||||
smartPlugRepository.updateTotalConsumption(SmartPlug.AVERAGE_CONSUMPTION_KW);
|
||||
final Collection<SmartPlug> c = smartPlugRepository.findByOn(true);
|
||||
c.forEach(s -> sensorSocketEndpoint.broadcast(s, sensorRepository.findUser(s.getId())));
|
||||
}
|
||||
}
|
|
@ -1,13 +1,12 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.socket;
|
||||
|
||||
import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.didThrow;
|
||||
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonConfig;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import com.google.gson.Gson;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import javax.websocket.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -57,12 +56,19 @@ public class SensorSocketEndpoint extends Endpoint {
|
|||
* @param u the user to which to send the message
|
||||
* @return number of successful transfer
|
||||
*/
|
||||
public long broadcast(Object message, User u) {
|
||||
final Collection<Session> sessions = authorizedClients.get(u);
|
||||
return sessions.stream()
|
||||
.parallel()
|
||||
.filter(didThrow(s -> s.getBasicRemote().sendText(gson.toJson(message))))
|
||||
.count();
|
||||
public void broadcast(Object message, User u) {
|
||||
final HashSet<Session> sessions = new HashSet<>(authorizedClients.get(u));
|
||||
for (Session s : sessions) {
|
||||
try {
|
||||
if (s.isOpen()) {
|
||||
s.getBasicRemote().sendText(gson.toJson(message));
|
||||
} else {
|
||||
authorizedClients.remove(u, s);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
35
src/main/resources/application-dev.properties
Normal file
35
src/main/resources/application-dev.properties
Normal file
|
@ -0,0 +1,35 @@
|
|||
spring.http.converters.preferred-json-mapper=gson
|
||||
spring.datasource.url=jdbc:postgresql://localhost:5432/smarthut
|
||||
spring.datasource.username=postgres
|
||||
spring.datasource.password=
|
||||
|
||||
# Hibernate properties
|
||||
spring.jpa.database=POSTGRESQL
|
||||
spring.jpa.show-sql=false
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
|
||||
spring.jpa.properties.hibernate.format_sql=true
|
||||
|
||||
jwt.secret=thiskeymustbeverylongorthethingcomplainssoiamjustgoingtowritehereabunchofgarbageciaomamma
|
||||
|
||||
spring.mail.test-connection=true
|
||||
spring.mail.host=smtp.gmail.com
|
||||
spring.mail.port=587
|
||||
spring.mail.properties.mail.smtp.starttls.enable=true
|
||||
spring.mail.username=smarthut.sm@gmail.com
|
||||
spring.mail.password=dcadvbagqfkwbfts
|
||||
spring.mail.properties.mail.smtp.starttls.required=true
|
||||
spring.mail.properties.mail.smtp.auth=true
|
||||
spring.mail.properties.mail.smtp.connectiontimeout=5000
|
||||
spring.mail.properties.mail.smtp.timeout=5000
|
||||
spring.mail.properties.mail.smtp.writetimeout=5000
|
||||
|
||||
email.registrationSubject=Complete your SmartHut.sm registration
|
||||
email.registration=To confirm your registration, please click here:
|
||||
email.registrationPath=http://localhost:8080/register/confirm-account?token=
|
||||
email.registrationRedirect=http://localhost:3000
|
||||
|
||||
email.resetpasswordSubject=SmartHut.sm password reset
|
||||
email.resetpassword=To reset your password, please click here:
|
||||
email.resetpasswordPath=http://localhost:3000/password-reset?token=
|
||||
email.resetPasswordRedirect=http://localhost:3000/conf-reset-pass
|
42
src/main/resources/application-prod.properties
Normal file
42
src/main/resources/application-prod.properties
Normal file
|
@ -0,0 +1,42 @@
|
|||
spring.http.converters.preferred-json-mapper=gson
|
||||
|
||||
# Database connection properties
|
||||
spring.datasource.url=${POSTGRES_JDBC}
|
||||
spring.datasource.username=${POSTGRES_USER}
|
||||
spring.datasource.password=${POSTGRES_PASS}
|
||||
|
||||
# Hibernate properties
|
||||
spring.jpa.database=POSTGRESQL
|
||||
spring.jpa.show-sql=false
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
|
||||
spring.jpa.properties.hibernate.format_sql=true
|
||||
|
||||
# JWT secret
|
||||
jwt.secret=${SECRET}
|
||||
|
||||
# Mail connection properties
|
||||
spring.mail.test-connection=true
|
||||
spring.mail.host=${MAIL_HOST}
|
||||
spring.mail.port=${MAIL_PORT}
|
||||
spring.mail.properties.mail.smtp.starttls.enable=${MAIL_STARTTLS}
|
||||
spring.mail.username=${MAIL_USER}
|
||||
spring.mail.password=${MAIL_PASS}
|
||||
spring.mail.properties.mail.smtp.starttls.required=${MAIL_STARTTLS}
|
||||
spring.mail.properties.mail.smtp.auth=true
|
||||
spring.mail.properties.mail.smtp.connectiontimeout=5000
|
||||
spring.mail.properties.mail.smtp.timeout=5000
|
||||
spring.mail.properties.mail.smtp.writetimeout=5000
|
||||
|
||||
# Registration email properties
|
||||
email.registrationSubject=Complete your SmartHut.sm registration
|
||||
email.registration=To confirm your registration, please click here:
|
||||
email.registrationPath=${BACKEND_URL}/register/confirm-account?token=
|
||||
email.registrationSuccess=${FRONTEND_URL}
|
||||
|
||||
|
||||
# Password reset email properties
|
||||
email.resetpasswordSubject=SmartHut.sm password reset
|
||||
email.resetpassword=To reset your password, please click here:
|
||||
email.resetpasswordPath=${FRONTEND_URL}/password-reset?token=
|
||||
email.resetPasswordSuccess=${FRONTEND_URL}/conf-reset-pass
|
|
@ -1,33 +1 @@
|
|||
spring.http.converters.preferred-json-mapper=gson
|
||||
spring.datasource.url=jdbc:postgresql://localhost:5432/smarthut
|
||||
spring.datasource.username=postgres
|
||||
spring.datasource.password=
|
||||
|
||||
# Hibernate properties
|
||||
spring.jpa.database=POSTGRESQL
|
||||
spring.jpa.show-sql=true
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
|
||||
spring.jpa.properties.hibernate.format_sql=true
|
||||
|
||||
jwt.secret=thiskeymustbeverylongorthethingcomplainssoiamjustgoingtowritehereabunchofgarbageciaomamma
|
||||
|
||||
spring.mail.test-connection=true
|
||||
spring.mail.host=smtp.gmail.com
|
||||
spring.mail.port=587
|
||||
spring.mail.properties.mail.smtp.starttls.enable=true
|
||||
spring.mail.username=smarthut.sm@gmail.com
|
||||
spring.mail.password=dcadvbagqfkwbfts
|
||||
spring.mail.properties.mail.smtp.starttls.required=true
|
||||
spring.mail.properties.mail.smtp.auth=true
|
||||
spring.mail.properties.mail.smtp.connectiontimeout=5000
|
||||
spring.mail.properties.mail.smtp.timeout=5000
|
||||
spring.mail.properties.mail.smtp.writetimeout=5000
|
||||
|
||||
email.registrationSubject=Complete your SmartHut.sm registration
|
||||
email.registration=To confirm your registration, please click here:
|
||||
email.registrationPath=http://localhost:8080/register/confirm-account?token=
|
||||
|
||||
email.resetpasswordSubject=SmartHut.sm password reset
|
||||
email.resetpassword=To reset your password, please click here:
|
||||
email.resetpasswordPath=http://localhost:3000/password-reset?token=
|
||||
spring.profiles.active=${SMARTHUT_THIS_VALUE_IS_PROD_IF_THIS_IS_A_CONTAINER_PIZZOCCHERI:dev}
|
|
@ -8,6 +8,8 @@ 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.error.DuplicateRegistrationException;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UnauthorizedException;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.util.Map;
|
||||
|
@ -25,6 +27,10 @@ public class AuthenticationTests extends SmartHutTest {
|
|||
|
||||
@Autowired private TestRestTemplate restTemplate;
|
||||
|
||||
@Autowired private UserRepository userRepository;
|
||||
|
||||
@Autowired private ConfirmationTokenRepository tokenRepository;
|
||||
|
||||
private UserRegistrationRequest getDisabledUser() {
|
||||
final UserRegistrationRequest disabledUser = new UserRegistrationRequest();
|
||||
disabledUser.setName("Disabled User");
|
||||
|
@ -34,15 +40,6 @@ public class AuthenticationTests extends SmartHutTest {
|
|||
return disabledUser;
|
||||
}
|
||||
|
||||
private static final UserRegistrationRequest enabledUser = new UserRegistrationRequest();
|
||||
|
||||
static {
|
||||
enabledUser.setName("Enabled User");
|
||||
enabledUser.setEmail("enabled@example.com");
|
||||
enabledUser.setUsername("enabled");
|
||||
enabledUser.setPassword("password");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() {
|
||||
final ResponseEntity<OkResponse> res =
|
||||
|
@ -50,12 +47,7 @@ public class AuthenticationTests extends SmartHutTest {
|
|||
this.url("/register"), getDisabledUser(), OkResponse.class);
|
||||
assertThat(res.getStatusCode().equals(HttpStatus.OK));
|
||||
|
||||
final ResponseEntity<OkResponse> res2 =
|
||||
this.restTemplate.postForEntity(
|
||||
this.url("/register"), enabledUser, OkResponse.class);
|
||||
assertThat(res2.getStatusCode().equals(HttpStatus.OK));
|
||||
|
||||
// TODO: email confirmation for enabledUser
|
||||
registerTestUser(restTemplate, userRepository, tokenRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -230,4 +222,18 @@ public class AuthenticationTests extends SmartHutTest {
|
|||
assertThat(res.getBody() != null);
|
||||
assertThat(res.getBody().isUserDisabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginShouldReturnTokenWithEnabledUser() {
|
||||
final JWTRequest request = new JWTRequest();
|
||||
request.setUsernameOrEmail("enabled");
|
||||
request.setPassword("password");
|
||||
|
||||
final ResponseEntity<JWTResponse> res =
|
||||
this.restTemplate.postForEntity(url("/auth/login"), request, JWTResponse.class);
|
||||
assertThat(res.getStatusCode().equals(HttpStatus.OK));
|
||||
assertThat(res.getBody() != null);
|
||||
assertThat(res.getBody().getToken() != null);
|
||||
assertThat(!res.getBody().getToken().isEmpty());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableLight;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.KnobDimmer;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@DisplayName("A Knob Dimmer")
|
||||
public class KnobDimmerTests {
|
||||
|
||||
KnobDimmer knobDimmer;
|
||||
|
||||
@BeforeEach
|
||||
public void createNewKnobDimmer() {
|
||||
this.knobDimmer = new KnobDimmer();
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName(" when multiple lights are present")
|
||||
class MultipleLights {
|
||||
|
||||
@BeforeEach
|
||||
public void setLights() {
|
||||
DimmableLight dl;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
dl = new DimmableLight();
|
||||
dl.setIntensity(10);
|
||||
;
|
||||
knobDimmer.addDimmableLight(dl);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName(" set the intensity ")
|
||||
public void increase() {
|
||||
knobDimmer.setLightIntensity(30);
|
||||
for (DimmableLight dl : knobDimmer.getOutputs()) {
|
||||
assertEquals(30, dl.getIntensity());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,18 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut;
|
||||
|
||||
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.models.ConfirmationToken;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
public abstract class SmartHutTest {
|
||||
private boolean setupDone = false;
|
||||
|
@ -15,6 +27,38 @@ public abstract class SmartHutTest {
|
|||
|
||||
protected void setUp() {}
|
||||
|
||||
protected static final UserRegistrationRequest enabledUser = new UserRegistrationRequest();
|
||||
|
||||
static {
|
||||
enabledUser.setName("Enabled User");
|
||||
enabledUser.setEmail("enabled@example.com");
|
||||
enabledUser.setUsername("enabled");
|
||||
enabledUser.setPassword("password");
|
||||
}
|
||||
|
||||
protected void registerTestUser(
|
||||
final TestRestTemplate restTemplate,
|
||||
final UserRepository userRepository,
|
||||
final ConfirmationTokenRepository tokenRepository) {
|
||||
final ResponseEntity<OkResponse> res2 =
|
||||
restTemplate.postForEntity(this.url("/register"), enabledUser, OkResponse.class);
|
||||
assertThat(res2.getStatusCode().equals(HttpStatus.OK));
|
||||
|
||||
final User persistedEnabledUser = userRepository.findByUsername("enabled");
|
||||
final ConfirmationToken token = tokenRepository.findByUser(persistedEnabledUser);
|
||||
|
||||
final ResponseEntity<Void> res3 =
|
||||
WebClient.create(getBaseURL())
|
||||
.get()
|
||||
.uri("/register/confirm-account?token=" + token.getConfirmationToken())
|
||||
.retrieve()
|
||||
.toBodilessEntity()
|
||||
.block();
|
||||
|
||||
assertThat(res3.getStatusCode().is2xxSuccessful());
|
||||
assertThat(userRepository.findByUsername("enabled").getEnabled());
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUpHack() {
|
||||
if (!setupDone) {
|
||||
|
|
|
@ -29,7 +29,9 @@ server.port = 2000
|
|||
email.registrationSubject=Complete your SmartHut.sm registration
|
||||
email.registration=To confirm your registration, please click here:
|
||||
email.registrationPath=http://localhost:8080/register/confirm-account?token=
|
||||
email.registrationRedirect=http://localhost:3000
|
||||
|
||||
email.resetpasswordSubject=SmartHut.sm password reset
|
||||
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
|
Loading…
Reference in a new issue