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/**
|
.idea/**
|
||||||
|
|
||||||
|
data/**/*
|
||||||
|
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
|
|
||||||
# Compiled class file
|
# Compiled class file
|
||||||
|
@ -138,4 +140,4 @@ gradle-app.setting
|
||||||
# gradle/wrapper/gradle-wrapper.properties
|
# gradle/wrapper/gradle-wrapper.properties
|
||||||
|
|
||||||
# IntelliJ
|
# IntelliJ
|
||||||
*.iml
|
*.iml
|
||||||
|
|
|
@ -62,4 +62,4 @@ code_quality:
|
||||||
paths:
|
paths:
|
||||||
- build/reports/cpd/cpdCheck.xml
|
- build/reports/cpd/cpdCheck.xml
|
||||||
#create a report on the quality of the code
|
#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
|
FROM openjdk:13-jdk-alpine
|
||||||
ENV DB_URL ""
|
|
||||||
ENV DB_USER ""
|
|
||||||
ENV DB_PASS ""
|
|
||||||
ARG JAR_FILE=build/libs/smarthut*.jar
|
ARG JAR_FILE=build/libs/smarthut*.jar
|
||||||
|
|
||||||
COPY ${JAR_FILE} app.jar
|
COPY ${JAR_FILE} app.jar
|
||||||
|
|
||||||
|
ENV SMARTHUT_THIS_VALUE_IS_PROD_IF_THIS_IS_A_CONTAINER_PIZZOCCHERI=prod
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
ENTRYPOINT ["java","-jar","/app.jar"]
|
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
|
# 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 "de.aaschmid.cpd" version "3.1"
|
||||||
id 'java'
|
id 'java'
|
||||||
}
|
}
|
||||||
|
|
||||||
group = 'ch.usi.inf.sa4.sanmarinoes'
|
group = 'ch.usi.inf.sa4.sanmarinoes'
|
||||||
version = '0.0.1-SNAPSHOT'
|
version = '0.0.1-SNAPSHOT'
|
||||||
sourceCompatibility = '11'
|
sourceCompatibility = '11'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final'
|
compile 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final'
|
||||||
compile "org.springframework.boot:spring-boot-starter-websocket"
|
compile "org.springframework.boot:spring-boot-starter-websocket"
|
||||||
|
|
||||||
implementation 'org.springframework.boot:spring-boot-starter'
|
implementation 'org.springframework.boot:spring-boot-starter'
|
||||||
|
|
||||||
implementation 'com.sun.mail:javax.mail:1.6.2'
|
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-data-jpa'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
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-swagger2:2.9.2'
|
||||||
compile 'io.springfox:springfox-swagger-ui:2.9.2'
|
compile 'io.springfox:springfox-swagger-ui:2.9.2'
|
||||||
compile 'org.springframework.boot:spring-boot-configuration-processor'
|
compile 'org.springframework.boot:spring-boot-configuration-processor'
|
||||||
|
testCompile 'org.springframework.boot:spring-boot-starter-webflux'
|
||||||
implementation('org.springframework.boot:spring-boot-starter-web') {
|
implementation('org.springframework.boot:spring-boot-starter-web') {
|
||||||
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-json'
|
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-json'
|
||||||
}
|
}
|
||||||
|
|
||||||
testImplementation('org.springframework.boot:spring-boot-starter-test') {
|
testImplementation('org.springframework.boot:spring-boot-starter-test') {
|
||||||
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
|
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
|
||||||
}
|
}
|
||||||
|
|
||||||
testImplementation 'org.springframework.security:spring-security-test'
|
testImplementation 'org.springframework.security:spring-security-test'
|
||||||
testImplementation 'com.h2database:h2:1.4.200'
|
testImplementation 'com.h2database:h2:1.4.200'
|
||||||
|
|
||||||
// Fixes https://stackoverflow.com/a/60455550
|
// Fixes https://stackoverflow.com/a/60455550
|
||||||
testImplementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.11'
|
testImplementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.11'
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
|
||||||
import javax.servlet.*;
|
import javax.servlet.*;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
@ -14,23 +13,13 @@ import org.springframework.stereotype.Component;
|
||||||
@Component
|
@Component
|
||||||
public class CORSFilter implements Filter {
|
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-Origin", "*");
|
||||||
response.setHeader("Access-Control-Allow-Methods", "HEAD, PUT, POST, GET, OPTIONS, DELETE");
|
response.setHeader("Access-Control-Allow-Methods", "*");
|
||||||
response.setHeader("Access-Control-Max-Age", "3600");
|
response.setHeader("Access-Control-Allow-Headers", "*");
|
||||||
response.setHeader(
|
response.setHeader("Access-Control-Allow-Credentials", "true");
|
||||||
"Access-Control-Allow-Headers",
|
response.setHeader("Access-Control-Expose-Headers", "*");
|
||||||
String.join(
|
response.setHeader("Access-Control-Max-Age", "6".repeat(99));
|
||||||
",",
|
|
||||||
List.of(
|
|
||||||
"Access-Control-Allow-Headers",
|
|
||||||
"Origin",
|
|
||||||
"Accept",
|
|
||||||
"X-Requested-With",
|
|
||||||
"Authorization",
|
|
||||||
"Content-Type",
|
|
||||||
"Access-Control-Request-Method",
|
|
||||||
"Access-Control-Request-Headers")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -30,17 +30,21 @@ public class EmailConfigurationService {
|
||||||
*/
|
*/
|
||||||
@NotNull private String registrationPath;
|
@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 */
|
/** The email subject for a reset password email */
|
||||||
@NotNull private String resetPasswordSubject;
|
@NotNull private String resetPasswordSubject;
|
||||||
|
|
||||||
/** The text in the email body preceding the confirmation URL for a reset password email */
|
/** The text in the email body preceding the confirmation URL for a reset password email */
|
||||||
@NotNull private String resetPassword;
|
@NotNull private String resetPassword;
|
||||||
|
|
||||||
/**
|
@NotNull private String resetPasswordRedirect;
|
||||||
* The URL to follow for password reset email confirmation. Has to end with the start of a query
|
|
||||||
* parameter
|
@NotNull private String registrationRedirect;
|
||||||
*/
|
|
||||||
@NotNull private String resetPasswordPath;
|
|
||||||
|
|
||||||
public String getRegistrationSubject() {
|
public String getRegistrationSubject() {
|
||||||
return registrationSubject;
|
return registrationSubject;
|
||||||
|
@ -89,4 +93,20 @@ public class EmailConfigurationService {
|
||||||
public void setResetPasswordPath(String resetPasswordPath) {
|
public void setResetPasswordPath(String resetPasswordPath) {
|
||||||
this.resetPasswordPath = 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;
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||||
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
@ -75,7 +74,8 @@ public class SpringFoxConfig {
|
||||||
.or(PathSelectors.regex("/sensor.*")::apply)
|
.or(PathSelectors.regex("/sensor.*")::apply)
|
||||||
.or(PathSelectors.regex("/smartPlug.*")::apply)
|
.or(PathSelectors.regex("/smartPlug.*")::apply)
|
||||||
.or(PathSelectors.regex("/switch.*")::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.config.JWTTokenUtils;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTRequest;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTRequest;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTResponse;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTResponse;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserUpdateRequest;
|
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UnauthorizedException;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UnauthorizedException;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UserNotFoundException;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UserNotFoundException;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||||
import io.swagger.annotations.Authorization;
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
@ -72,19 +70,9 @@ public class AuthenticationController {
|
||||||
return new JWTResponse(token);
|
return new JWTResponse(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Authorization(value = "Bearer")
|
@GetMapping("/profile")
|
||||||
@PatchMapping("/update")
|
public User profile(final Principal principal) {
|
||||||
public User update(
|
return userRepository.findByUsername(principal.getName());
|
||||||
@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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void authenticate(String username, String password) throws UnauthorizedException {
|
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.dto.GenericDeviceSaveReguest;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
@ -52,10 +53,13 @@ public class ButtonDimmerController
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/dim")
|
@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 {
|
throws NotFoundException {
|
||||||
final ButtonDimmer buttonDimmer =
|
final ButtonDimmer buttonDimmer =
|
||||||
buttonDimmerRepository.findById(bd.getId()).orElseThrow(NotFoundException::new);
|
buttonDimmerRepository
|
||||||
|
.findByIdAndUsername(bd.getId(), principal.getName())
|
||||||
|
.orElseThrow(NotFoundException::new);
|
||||||
|
|
||||||
switch (bd.getDimType()) {
|
switch (bd.getDimType()) {
|
||||||
case UP:
|
case UP:
|
||||||
|
|
|
@ -27,16 +27,17 @@ public class DeviceController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping
|
@PutMapping
|
||||||
public Device update(@Valid @RequestBody DeviceSaveRequest deviceSaveRequest)
|
public Device update(
|
||||||
|
@Valid @RequestBody DeviceSaveRequest deviceSaveRequest, final Principal principal)
|
||||||
throws NotFoundException, BadDataException {
|
throws NotFoundException, BadDataException {
|
||||||
final Device d =
|
final Device d =
|
||||||
deviceRepository
|
deviceRepository
|
||||||
.findById(deviceSaveRequest.getId())
|
.findByIdAndUsername(deviceSaveRequest.getId(), principal.getName())
|
||||||
.orElseThrow(NotFoundException::new);
|
.orElseThrow(NotFoundException::new);
|
||||||
|
|
||||||
// check if roomId is valid
|
// check if roomId is valid
|
||||||
roomRepository
|
roomRepository
|
||||||
.findById(deviceSaveRequest.getRoomId())
|
.findByIdAndUsername(deviceSaveRequest.getRoomId(), principal.getName())
|
||||||
.orElseThrow(() -> new BadDataException("roomId is not a valid room id"));
|
.orElseThrow(() -> new BadDataException("roomId is not a valid room id"));
|
||||||
|
|
||||||
d.setRoomId(deviceSaveRequest.getRoomId());
|
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.error.NotFoundException;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableLight;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableLight;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableLightRepository;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableLightRepository;
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -43,10 +44,14 @@ public class DimmableLightController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping
|
@PutMapping
|
||||||
public DimmableLight update(@Valid @RequestBody DimmableLightSaveRequest sp)
|
public DimmableLight update(
|
||||||
|
@Valid @RequestBody DimmableLightSaveRequest sp, final Principal principal)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
return save(
|
return save(
|
||||||
dimmableLightService.findById(sp.getId()).orElseThrow(NotFoundException::new), sp);
|
dimmableLightService
|
||||||
|
.findByIdAndUsername(sp.getId(), principal.getName())
|
||||||
|
.orElseThrow(NotFoundException::new),
|
||||||
|
sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}")
|
@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.dto.KnobDimmerDimRequest;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
@ -53,10 +54,13 @@ public class KnobDimmerController
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/dimTo")
|
@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 {
|
throws NotFoundException {
|
||||||
final KnobDimmer dimmer =
|
final KnobDimmer dimmer =
|
||||||
knobDimmerRepository.findById(bd.getId()).orElseThrow(NotFoundException::new);
|
knobDimmerRepository
|
||||||
|
.findByIdAndUsername(bd.getId(), principal.getName())
|
||||||
|
.orElseThrow(NotFoundException::new);
|
||||||
|
|
||||||
dimmer.setLightIntensity(bd.getIntensity());
|
dimmer.setLightIntensity(bd.getIntensity());
|
||||||
dimmableLightRepository.saveAll(dimmer.getOutputs());
|
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.error.NotFoundException;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RegularLight;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RegularLight;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RegularLightRepository;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RegularLightRepository;
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -50,10 +51,14 @@ public class RegularLightController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping
|
@PutMapping
|
||||||
public RegularLight update(@Valid @RequestBody RegularLightSaveRequest rl)
|
public RegularLight update(
|
||||||
|
@Valid @RequestBody RegularLightSaveRequest rl, final Principal principal)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
return save(
|
return save(
|
||||||
regularLightService.findById(rl.getId()).orElseThrow(NotFoundException::new), rl);
|
regularLightService
|
||||||
|
.findByIdAndUsername(rl.getId(), principal.getName())
|
||||||
|
.orElseThrow(NotFoundException::new),
|
||||||
|
rl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
|
|
|
@ -23,6 +23,12 @@ public class RoomController {
|
||||||
|
|
||||||
@Autowired private DeviceRepository<Device> deviceRepository;
|
@Autowired private DeviceRepository<Device> deviceRepository;
|
||||||
|
|
||||||
|
@Autowired private SwitchRepository switchRepository;
|
||||||
|
|
||||||
|
@Autowired private ButtonDimmerRepository buttonDimmerRepository;
|
||||||
|
|
||||||
|
@Autowired private KnobDimmerRepository knobDimmerRepository;
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public List<Room> findAll() {
|
public List<Room> findAll() {
|
||||||
return toList(roomRepository.findAll());
|
return toList(roomRepository.findAll());
|
||||||
|
@ -33,44 +39,57 @@ public class RoomController {
|
||||||
return roomRepository.findById(id).orElseThrow(NotFoundException::new);
|
return roomRepository.findById(id).orElseThrow(NotFoundException::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Room save(final RoomSaveRequest r, final Principal principal, boolean setWhenNull) {
|
@PostMapping
|
||||||
Room newRoom = new Room();
|
public @ResponseBody Room create(
|
||||||
|
@Valid @RequestBody RoomSaveRequest r, final Principal principal) {
|
||||||
|
|
||||||
final String username = principal.getName();
|
final String username = principal.getName();
|
||||||
final Long userId = userRepository.findByUsername(username).getId();
|
final Long userId = userRepository.findByUsername(username).getId();
|
||||||
final String img = r.getImage();
|
final String img = r.getImage();
|
||||||
final Room.Icon icon = r.getIcon();
|
final Room.Icon icon = r.getIcon();
|
||||||
|
|
||||||
|
final Room newRoom = new Room();
|
||||||
newRoom.setUserId(userId);
|
newRoom.setUserId(userId);
|
||||||
newRoom.setName(r.getName());
|
newRoom.setName(r.getName());
|
||||||
if (img != null) {
|
newRoom.setImage(img);
|
||||||
newRoom.setImage(img);
|
newRoom.setIcon(icon);
|
||||||
} else if (setWhenNull) {
|
|
||||||
newRoom.setImage(null);
|
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) {
|
if (icon != null) {
|
||||||
newRoom.setIcon(icon);
|
newRoom.setIcon(icon);
|
||||||
} else if (setWhenNull) {
|
|
||||||
newRoom.setIcon(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return roomRepository.save(newRoom);
|
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}")
|
@DeleteMapping("/{id}")
|
||||||
public void deleteById(@PathVariable("id") long id) {
|
public void deleteById(@PathVariable("id") long id) {
|
||||||
|
switchRepository.deleteAllByRoomId(id);
|
||||||
|
knobDimmerRepository.deleteAllByRoomId(id);
|
||||||
|
buttonDimmerRepository.deleteAllByRoomId(id);
|
||||||
roomRepository.deleteById(id);
|
roomRepository.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +98,7 @@ public class RoomController {
|
||||||
* id).
|
* id).
|
||||||
*/
|
*/
|
||||||
@GetMapping(path = "/{roomId}/devices")
|
@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);
|
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.dto.SmartPlugSaveRequest;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
@ -14,7 +15,7 @@ import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@RequestMapping("/smartplug")
|
@RequestMapping("/smartPlug")
|
||||||
public class SmartPlugController {
|
public class SmartPlugController {
|
||||||
|
|
||||||
@Autowired private SmartPlugRepository smartPlugRepository;
|
@Autowired private SmartPlugRepository smartPlugRepository;
|
||||||
|
@ -44,9 +45,25 @@ public class SmartPlugController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping
|
@PutMapping
|
||||||
public SmartPlug update(@Valid @RequestBody SmartPlugSaveRequest sp) throws NotFoundException {
|
public SmartPlug update(@Valid @RequestBody SmartPlugSaveRequest sp, final Principal principal)
|
||||||
|
throws NotFoundException {
|
||||||
return save(
|
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}")
|
@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.dto.SwitchOperationRequest;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
@ -55,9 +56,13 @@ public class SwitchController extends InputDeviceConnectionController<Switch, Sw
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/operate")
|
@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 {
|
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()) {
|
switch (sr.getType()) {
|
||||||
case ON:
|
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.User;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.EmailSenderService;
|
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.Valid;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
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
|
* @throws EmailTokenNotFoundException if given token is not a valid token for password reset
|
||||||
*/
|
*/
|
||||||
@PutMapping("/reset-password")
|
@PutMapping("/reset-password")
|
||||||
public OkResponse resetPassword(@Valid @RequestBody PasswordResetRequest resetRequest)
|
public OkResponse resetPassword(
|
||||||
throws EmailTokenNotFoundException {
|
@Valid @RequestBody PasswordResetRequest resetRequest,
|
||||||
|
final HttpServletResponse response)
|
||||||
|
throws EmailTokenNotFoundException, IOException {
|
||||||
final ConfirmationToken token =
|
final ConfirmationToken token =
|
||||||
confirmationTokenRepository.findByConfirmationToken(
|
confirmationTokenRepository.findByConfirmationToken(
|
||||||
resetRequest.getConfirmationToken());
|
resetRequest.getConfirmationToken());
|
||||||
|
@ -187,16 +191,17 @@ public class UserAccountController {
|
||||||
* confirmation
|
* confirmation
|
||||||
*/
|
*/
|
||||||
@GetMapping(value = "/confirm-account")
|
@GetMapping(value = "/confirm-account")
|
||||||
public OkResponse confirmUserAccount(@RequestParam("token") @NotNull String confirmationToken)
|
public void confirmUserAccount(
|
||||||
throws EmailTokenNotFoundException {
|
@RequestParam("token") @NotNull String confirmationToken,
|
||||||
|
final HttpServletResponse response)
|
||||||
|
throws EmailTokenNotFoundException, IOException {
|
||||||
final ConfirmationToken token =
|
final ConfirmationToken token =
|
||||||
confirmationTokenRepository.findByConfirmationToken(confirmationToken);
|
confirmationTokenRepository.findByConfirmationToken(confirmationToken);
|
||||||
|
|
||||||
if (token != null && !token.getResetPassword()) {
|
if (token != null && !token.getResetPassword()) {
|
||||||
token.getUser().setEnabled(true);
|
token.getUser().setEnabled(true);
|
||||||
userRepository.save(token.getUser());
|
userRepository.save(token.getUser());
|
||||||
// TODO: redirect to frontend
|
response.sendRedirect(emailConfig.getRegistrationRedirect());
|
||||||
return new OkResponse();
|
|
||||||
} else {
|
} else {
|
||||||
throw new EmailTokenNotFoundException();
|
throw new EmailTokenNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ public class DimmableLightSaveRequest {
|
||||||
|
|
||||||
/** The light intensity value. Goes from 0 (off) to 100 (on) */
|
/** The light intensity value. Goes from 0 (off) to 100 (on) */
|
||||||
@NotNull
|
@NotNull
|
||||||
@Min(1)
|
@Min(0)
|
||||||
@Max(100)
|
@Max(100)
|
||||||
private Integer intensity = 0;
|
private Integer intensity = 0;
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,9 @@ import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
public class RoomSaveRequest {
|
public class RoomSaveRequest {
|
||||||
|
|
||||||
|
/** Room identifier */
|
||||||
|
private long id;
|
||||||
|
|
||||||
@NotNull private Room.Icon icon;
|
@NotNull private Room.Icon icon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,6 +22,14 @@ public class RoomSaveRequest {
|
||||||
/** The user given name of this room (e.g. 'Master bedroom') */
|
/** The user given name of this room (e.g. 'Master bedroom') */
|
||||||
@NotNull private String name;
|
@NotNull private String name;
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ public class ButtonDimmer extends Dimmer {
|
||||||
private static final int DIM_INCREMENT = 10;
|
private static final int DIM_INCREMENT = 10;
|
||||||
|
|
||||||
public ButtonDimmer() {
|
public ButtonDimmer() {
|
||||||
super("button-dimmer");
|
super("buttonDimmer");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Increases the current intensity level of the dimmable light by DIM_INCREMENT */
|
/** Increases the current intensity level of the dimmable light by DIM_INCREMENT */
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
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> {
|
public interface ConfirmationTokenRepository extends CrudRepository<ConfirmationToken, String> {
|
||||||
ConfirmationToken findByConfirmationToken(String confirmationToken);
|
ConfirmationToken findByConfirmationToken(String confirmationToken);
|
||||||
|
|
||||||
|
ConfirmationToken findByUser(User user);
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
void deleteByUserAndResetPassword(User user, boolean resetPassword);
|
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
|
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
|
||||||
* a REST call.
|
* a REST call.
|
||||||
*/
|
*/
|
||||||
@Column(name = "room_id", nullable = false, unique = true)
|
@Column(name = "room_id", nullable = false)
|
||||||
@NotNull
|
@NotNull
|
||||||
private Long roomId;
|
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
|
* The name for the category of this particular device (e.g 'dimmer'). Not stored in the
|
||||||
* database but set thanks to constructors
|
* 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
|
* The way this device behaves in the automation flow. Not stored in the database but set thanks
|
||||||
* to constructors
|
* to constructors
|
||||||
*/
|
*/
|
||||||
@ApiModelProperty(hidden = true)
|
@Transient private final FlowType flowType;
|
||||||
@Transient
|
|
||||||
private final FlowType flowType;
|
|
||||||
|
|
||||||
public long getId() {
|
public long getId() {
|
||||||
return id;
|
return id;
|
||||||
|
|
|
@ -2,6 +2,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.CrudRepository;
|
import org.springframework.data.repository.CrudRepository;
|
||||||
import org.springframework.data.repository.query.Param;
|
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
|
* @param username a User's username
|
||||||
* @return an optional device, empty if none found
|
* @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")
|
@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);
|
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
|
* @param username the User's username
|
||||||
* @return all devices of that user
|
* @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")
|
@Query("SELECT d FROM Device d JOIN d.room r JOIN r.user u WHERE u.username = ?1")
|
||||||
List<T> findAllByUsername(String username);
|
List<T> findAllByUsername(String username);
|
||||||
|
|
||||||
|
@ -38,6 +41,7 @@ public interface DeviceRepository<T extends Device> extends CrudRepository<T, Lo
|
||||||
* @param deviceId the device id
|
* @param deviceId the device id
|
||||||
* @return a user object
|
* @return a user object
|
||||||
*/
|
*/
|
||||||
|
@Transactional
|
||||||
@Query("SELECT u FROM Device d JOIN d.room r JOIN r.user u WHERE d.id = ?1")
|
@Query("SELECT u FROM Device d JOIN d.room r JOIN r.user u WHERE d.id = ?1")
|
||||||
User findUser(Long deviceId);
|
User findUser(Long deviceId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ public class DimmableLight extends Switchable {
|
||||||
Connector.basic(KnobDimmer::getOutputs, DimmableLight::setDimmerId);
|
Connector.basic(KnobDimmer::getOutputs, DimmableLight::setDimmerId);
|
||||||
|
|
||||||
public DimmableLight() {
|
public DimmableLight() {
|
||||||
super("light");
|
super("dimmableLight");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
|
@ -39,6 +39,10 @@ public class DimmableLight extends Switchable {
|
||||||
@Max(100)
|
@Max(100)
|
||||||
private Integer intensity = 0;
|
private Integer intensity = 0;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Column(nullable = false)
|
||||||
|
private Integer oldIntensity = 100;
|
||||||
|
|
||||||
public Integer getIntensity() {
|
public Integer getIntensity() {
|
||||||
return intensity;
|
return intensity;
|
||||||
}
|
}
|
||||||
|
@ -55,8 +59,10 @@ public class DimmableLight extends Switchable {
|
||||||
this.intensity = 0;
|
this.intensity = 0;
|
||||||
} else if (intensity > 100) {
|
} else if (intensity > 100) {
|
||||||
this.intensity = 100;
|
this.intensity = 100;
|
||||||
|
this.oldIntensity = 100;
|
||||||
} else {
|
} else {
|
||||||
this.intensity = intensity;
|
this.intensity = intensity;
|
||||||
|
this.oldIntensity = intensity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,13 +73,13 @@ public class DimmableLight extends Switchable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOn(boolean on) {
|
public void setOn(boolean on) {
|
||||||
intensity = on ? 100 : 0;
|
intensity = on ? oldIntensity : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDimmerId(Long dimmerId) {
|
public void setDimmerId(Long dimmerId) {
|
||||||
this.dimmerId = dimmerId;
|
this.dimmerId = dimmerId;
|
||||||
super.setSwitchId(null);
|
super.setSwitchId(null);
|
||||||
};
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSwitchId(Long switchId) {
|
public void setSwitchId(Long switchId) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import javax.persistence.Entity;
|
||||||
import javax.persistence.Inheritance;
|
import javax.persistence.Inheritance;
|
||||||
import javax.persistence.InheritanceType;
|
import javax.persistence.InheritanceType;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.PreRemove;
|
||||||
|
|
||||||
/** Represents a generic dimmer input device */
|
/** Represents a generic dimmer input device */
|
||||||
@Entity
|
@Entity
|
||||||
|
@ -32,4 +33,11 @@ public abstract class Dimmer extends InputDevice {
|
||||||
public void addDimmableLight(DimmableLight dimmableLight) {
|
public void addDimmableLight(DimmableLight dimmableLight) {
|
||||||
lights.add(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 class KnobDimmer extends Dimmer {
|
||||||
|
|
||||||
public KnobDimmer() {
|
public KnobDimmer() {
|
||||||
super("knob-dimmer");
|
super("knobDimmer");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
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() {
|
public MotionSensor() {
|
||||||
super("motion-sensor");
|
super("motionSensor");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ public class RegularLight extends Switchable {
|
||||||
boolean on;
|
boolean on;
|
||||||
|
|
||||||
public RegularLight() {
|
public RegularLight() {
|
||||||
super("regular-light");
|
super("regularLight");
|
||||||
this.on = false;
|
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 ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@ -125,7 +127,6 @@ public class Room {
|
||||||
* https://www.baeldung.com/java-base64-image-string
|
* https://www.baeldung.com/java-base64-image-string
|
||||||
* https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html
|
* https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html
|
||||||
*/
|
*/
|
||||||
@Lob
|
|
||||||
@Column(name = "image", columnDefinition = "TEXT")
|
@Column(name = "image", columnDefinition = "TEXT")
|
||||||
private String image;
|
private String image;
|
||||||
|
|
||||||
|
@ -134,12 +135,16 @@ public class Room {
|
||||||
@GsonExclude
|
@GsonExclude
|
||||||
private User user;
|
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
|
* User that owns the house this room is in as a foreign key id. To use when updating and
|
||||||
* inserting from a REST call.
|
* inserting from a REST call.
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
@Column(name = "user_id", nullable = false, unique = true)
|
@Column(name = "user_id", nullable = false)
|
||||||
private Long userId;
|
private Long userId;
|
||||||
|
|
||||||
/** The user given name of this room (e.g. 'Master bedroom') */
|
/** The user given name of this room (e.g. 'Master bedroom') */
|
||||||
|
@ -187,6 +192,10 @@ public class Room {
|
||||||
this.image = image;
|
this.image = image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<Device> getDevices() {
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Room{" + "id=" + id + ", name='" + name + "\'}";
|
return "Room{" + "id=" + id + ", name='" + name + "\'}";
|
||||||
|
|
|
@ -1,5 +1,18 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
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;
|
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 */
|
/** 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;
|
private BigDecimal value;
|
||||||
|
|
||||||
/** The type of this sensor */
|
/** The type of this sensor */
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
@ -8,11 +9,28 @@ import javax.validation.constraints.NotNull;
|
||||||
@Entity
|
@Entity
|
||||||
public class SmartPlug extends Switchable {
|
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 */
|
/** Whether the smart plug is on */
|
||||||
@Column(name = "smart_plug_on", nullable = false)
|
@Column(name = "smart_plug_on", nullable = false)
|
||||||
@NotNull
|
@NotNull
|
||||||
private boolean on;
|
private boolean on;
|
||||||
|
|
||||||
|
public BigDecimal getTotalConsumption() {
|
||||||
|
return totalConsumption;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Resets the consuption meter */
|
||||||
|
public void resetTotalConsumption() {
|
||||||
|
totalConsumption = BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isOn() {
|
public boolean isOn() {
|
||||||
return on;
|
return on;
|
||||||
|
@ -24,6 +42,6 @@ public class SmartPlug extends Switchable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public SmartPlug() {
|
public SmartPlug() {
|
||||||
super("smart-plug");
|
super("smartPlug");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,24 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
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.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.PreRemove;
|
||||||
|
|
||||||
/** A switch input device */
|
/** A switch input device */
|
||||||
@Entity
|
@Entity
|
||||||
|
@ -51,4 +52,11 @@ public class Switch extends InputDevice {
|
||||||
public Set<Switchable> getOutputs() {
|
public Set<Switchable> getOutputs() {
|
||||||
return switchables;
|
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;
|
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;
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
|
@ -24,6 +25,7 @@ public class User {
|
||||||
|
|
||||||
/** A properly salted way to store the password */
|
/** A properly salted way to store the password */
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
|
@GsonExclude
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,7 +36,7 @@ public class User {
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
@ApiModelProperty(hidden = true)
|
@GsonExclude
|
||||||
private Boolean isEnabled = false;
|
private Boolean isEnabled = false;
|
||||||
|
|
||||||
public Long getId() {
|
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.MotionSensorController;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.controller.SensorController;
|
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.*;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Sensor;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.SensorRepository;
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.StreamSupport;
|
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.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
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
|
@Component
|
||||||
public class SensorUpdateTasks {
|
public class UpdateTasks {
|
||||||
|
|
||||||
@Autowired private SensorRepository sensorRepository;
|
@Autowired private SensorRepository sensorRepository;
|
||||||
|
|
||||||
@Autowired private MotionSensorRepository motionSensorRepository;
|
@Autowired private MotionSensorRepository motionSensorRepository;
|
||||||
|
|
||||||
|
@Autowired private SmartPlugRepository smartPlugRepository;
|
||||||
|
|
||||||
@Autowired private SensorController sensorController;
|
@Autowired private SensorController sensorController;
|
||||||
|
|
||||||
@Autowired private MotionSensorController motionSensorController;
|
@Autowired private MotionSensorController motionSensorController;
|
||||||
|
|
||||||
|
@Autowired private SensorSocketEndpoint sensorSocketEndpoint;
|
||||||
|
|
||||||
/** Generates fake sensor updates every two seconds with a +/- 1.25% error */
|
/** Generates fake sensor updates every two seconds with a +/- 1.25% error */
|
||||||
@Scheduled(fixedRate = 2000)
|
@Scheduled(fixedRate = 2000)
|
||||||
public void sensorFakeUpdate() {
|
public void sensorFakeUpdate() {
|
||||||
|
@ -59,4 +66,12 @@ public class SensorUpdateTasks {
|
||||||
sensor, false));
|
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;
|
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.config.GsonConfig;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
|
||||||
import com.google.common.collect.HashMultimap;
|
import com.google.common.collect.HashMultimap;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.common.collect.Multimaps;
|
import com.google.common.collect.Multimaps;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import javax.websocket.*;
|
import javax.websocket.*;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
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
|
* @param u the user to which to send the message
|
||||||
* @return number of successful transfer
|
* @return number of successful transfer
|
||||||
*/
|
*/
|
||||||
public long broadcast(Object message, User u) {
|
public void broadcast(Object message, User u) {
|
||||||
final Collection<Session> sessions = authorizedClients.get(u);
|
final HashSet<Session> sessions = new HashSet<>(authorizedClients.get(u));
|
||||||
return sessions.stream()
|
for (Session s : sessions) {
|
||||||
.parallel()
|
try {
|
||||||
.filter(didThrow(s -> s.getBasicRemote().sendText(gson.toJson(message))))
|
if (s.isOpen()) {
|
||||||
.count();
|
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.profiles.active=${SMARTHUT_THIS_VALUE_IS_PROD_IF_THIS_IS_A_CONTAINER_PIZZOCCHERI:dev}
|
||||||
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=
|
|
|
@ -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.dto.UserRegistrationRequest;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateRegistrationException;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateRegistrationException;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UnauthorizedException;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UnauthorizedException;
|
||||||
|
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.JsonArray;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -25,6 +27,10 @@ public class AuthenticationTests extends SmartHutTest {
|
||||||
|
|
||||||
@Autowired private TestRestTemplate restTemplate;
|
@Autowired private TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Autowired private UserRepository userRepository;
|
||||||
|
|
||||||
|
@Autowired private ConfirmationTokenRepository tokenRepository;
|
||||||
|
|
||||||
private UserRegistrationRequest getDisabledUser() {
|
private UserRegistrationRequest getDisabledUser() {
|
||||||
final UserRegistrationRequest disabledUser = new UserRegistrationRequest();
|
final UserRegistrationRequest disabledUser = new UserRegistrationRequest();
|
||||||
disabledUser.setName("Disabled User");
|
disabledUser.setName("Disabled User");
|
||||||
|
@ -34,15 +40,6 @@ public class AuthenticationTests extends SmartHutTest {
|
||||||
return disabledUser;
|
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
|
@Override
|
||||||
protected void setUp() {
|
protected void setUp() {
|
||||||
final ResponseEntity<OkResponse> res =
|
final ResponseEntity<OkResponse> res =
|
||||||
|
@ -50,12 +47,7 @@ public class AuthenticationTests extends SmartHutTest {
|
||||||
this.url("/register"), getDisabledUser(), OkResponse.class);
|
this.url("/register"), getDisabledUser(), OkResponse.class);
|
||||||
assertThat(res.getStatusCode().equals(HttpStatus.OK));
|
assertThat(res.getStatusCode().equals(HttpStatus.OK));
|
||||||
|
|
||||||
final ResponseEntity<OkResponse> res2 =
|
registerTestUser(restTemplate, userRepository, tokenRepository);
|
||||||
this.restTemplate.postForEntity(
|
|
||||||
this.url("/register"), enabledUser, OkResponse.class);
|
|
||||||
assertThat(res2.getStatusCode().equals(HttpStatus.OK));
|
|
||||||
|
|
||||||
// TODO: email confirmation for enabledUser
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -230,4 +222,18 @@ public class AuthenticationTests extends SmartHutTest {
|
||||||
assertThat(res.getBody() != null);
|
assertThat(res.getBody() != null);
|
||||||
assertThat(res.getBody().isUserDisabled());
|
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;
|
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.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 {
|
public abstract class SmartHutTest {
|
||||||
private boolean setupDone = false;
|
private boolean setupDone = false;
|
||||||
|
@ -15,6 +27,38 @@ public abstract class SmartHutTest {
|
||||||
|
|
||||||
protected void setUp() {}
|
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
|
@BeforeEach
|
||||||
void setUpHack() {
|
void setUpHack() {
|
||||||
if (!setupDone) {
|
if (!setupDone) {
|
||||||
|
|
|
@ -29,7 +29,9 @@ server.port = 2000
|
||||||
email.registrationSubject=Complete your SmartHut.sm registration
|
email.registrationSubject=Complete your SmartHut.sm registration
|
||||||
email.registration=To confirm your registration, please click here:
|
email.registration=To confirm your registration, please click here:
|
||||||
email.registrationPath=http://localhost:8080/register/confirm-account?token=
|
email.registrationPath=http://localhost:8080/register/confirm-account?token=
|
||||||
|
email.registrationRedirect=http://localhost:3000
|
||||||
|
|
||||||
email.resetpasswordSubject=SmartHut.sm password reset
|
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
|
Loading…
Reference in a new issue