Merge branch 'dev' into 'master'
Milestone 1 deployment (attempt 1) See merge request sa4-2020/the-sanmarinoes/backend!67
This commit is contained in:
commit
1724589135
111 changed files with 5681 additions and 0 deletions
143
.gitignore
vendored
Normal file
143
.gitignore
vendored
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
.idea/**
|
||||||
|
|
||||||
|
data/**/*
|
||||||
|
|
||||||
|
**/.DS_Store
|
||||||
|
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid
|
||||||
|
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
.gradle
|
||||||
|
/build/
|
||||||
|
|
||||||
|
# Ignore Gradle GUI config
|
||||||
|
gradle-app.setting
|
||||||
|
|
||||||
|
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||||
|
!gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Cache of project
|
||||||
|
.gradletasknamecache
|
||||||
|
|
||||||
|
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||||
|
# gradle/wrapper/gradle-wrapper.properties
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
*.iml
|
65
.gitlab-ci.yml
Normal file
65
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
#Trying to set up the CI, probably won't work
|
||||||
|
image: gradle:jdk13
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
- test
|
||||||
|
- code_quality
|
||||||
|
- deploy
|
||||||
|
|
||||||
|
#Sets up the docker
|
||||||
|
smarthut_deploy:
|
||||||
|
stage: deploy
|
||||||
|
image: docker:latest
|
||||||
|
tags:
|
||||||
|
- dind
|
||||||
|
services:
|
||||||
|
- docker:dind
|
||||||
|
variables:
|
||||||
|
DOCKER_DRIVER: overlay
|
||||||
|
before_script:
|
||||||
|
- docker version
|
||||||
|
- docker info
|
||||||
|
- docker login -u smarthutsm -p $CI_DOCKER_PASS #GiovanniRoberto
|
||||||
|
script:
|
||||||
|
- "docker build -t smarthutsm/smarthut-backend:${CI_COMMIT_BRANCH} --pull ."
|
||||||
|
- "docker push smarthutsm/smarthut-backend:${CI_COMMIT_BRANCH}"
|
||||||
|
after_script:
|
||||||
|
- docker logout
|
||||||
|
only:
|
||||||
|
- dev
|
||||||
|
- master
|
||||||
|
|
||||||
|
#base checks for the code
|
||||||
|
build:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- gradle clean
|
||||||
|
- gradle assemble
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- build/libs/*.jar
|
||||||
|
expire_in: 1 week
|
||||||
|
|
||||||
|
#Runs the various tests and creates a report on the test coverage
|
||||||
|
test:
|
||||||
|
stage: test
|
||||||
|
script:
|
||||||
|
- gradle test
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- build/test-results/test/TEST-*.xml
|
||||||
|
reports:
|
||||||
|
junit: build/test-results/test/TEST-*.xml
|
||||||
|
|
||||||
|
#Runs a quality check on the code and creates a report on the codes
|
||||||
|
code_quality:
|
||||||
|
stage: code_quality
|
||||||
|
allow_failure: true
|
||||||
|
script:
|
||||||
|
- gradle cpdCheck
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- build/reports/cpd/cpdCheck.xml
|
||||||
|
#create a report on the quality of the code
|
||||||
|
expose_as: 'Code Quality Report'
|
9
Dockerfile
Normal file
9
Dockerfile
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
FROM openjdk:13-jdk-alpine
|
||||||
|
|
||||||
|
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"]
|
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
|
||||||
|
```
|
||||||
|
|
44
build.gradle
Normal file
44
build.gradle
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
plugins {
|
||||||
|
id 'org.springframework.boot' version '2.2.4.RELEASE'
|
||||||
|
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
|
||||||
|
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'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-mail'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-websocket'
|
||||||
|
implementation 'org.springframework:spring-websocket:5.2.4.RELEASE'
|
||||||
|
implementation 'io.jsonwebtoken:jjwt:0.9.1'
|
||||||
|
implementation 'org.springframework.security:spring-security-web'
|
||||||
|
implementation 'org.postgresql:postgresql'
|
||||||
|
implementation 'com.google.code.gson:gson'
|
||||||
|
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()
|
||||||
|
}
|
BIN
docs/er.pdf
Normal file
BIN
docs/er.pdf
Normal file
Binary file not shown.
14
git-hooks/format.sh
Executable file
14
git-hooks/format.sh
Executable file
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/sh -e
|
||||||
|
jar_version=1.6
|
||||||
|
jar_dir="$HOME/.local/share/java"
|
||||||
|
jar_file="$jar_dir/google-java-format-$jar_version-all-deps.jar"
|
||||||
|
java_cmd="java"
|
||||||
|
|
||||||
|
# download jar file if missing
|
||||||
|
if [ ! -f "$jar_file" ]; then
|
||||||
|
mkdir -p "$jar_dir"
|
||||||
|
wget -O "$jar_file" https://github.com/google/google-java-format/releases/download/google-java-format-$jar_version/google-java-format-$jar_version-all-deps.jar
|
||||||
|
fi
|
||||||
|
|
||||||
|
# execute formatter
|
||||||
|
$java_cmd -jar "$jar_file" $@
|
20
git-hooks/pre-commit.sh
Executable file
20
git-hooks/pre-commit.sh
Executable file
|
@ -0,0 +1,20 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Java formatter running..."
|
||||||
|
|
||||||
|
format_cmd="$(dirname $(realpath "$0"))/format.sh"
|
||||||
|
|
||||||
|
# skip if NO_VERIFY env var set
|
||||||
|
if [ "$NO_VERIFY" ]; then
|
||||||
|
echo 'google-java-format skipped' 1>&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# list all added/copied/modified/renamed java files
|
||||||
|
files="`git diff --staged --name-only --diff-filter=ACMR | egrep -a '.java$' | tr \"\\n\" \" \"`"
|
||||||
|
for f in $files; do
|
||||||
|
$format_cmd --aosp -i "$f"
|
||||||
|
git add -f "$f"
|
||||||
|
done
|
19
git-hooks/setup.sh
Executable file
19
git-hooks/setup.sh
Executable file
|
@ -0,0 +1,19 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if ! git remote get-url origin | grep "lab.si.usi.ch" >/dev/null 2>/dev/null; then
|
||||||
|
echo "Not in the project!"
|
||||||
|
echo "Call this script while in the root directory of the backend project";
|
||||||
|
exit 1;
|
||||||
|
elif ! [ -d "./git-hooks" ]; then
|
||||||
|
echo "Not in the right directory!"
|
||||||
|
echo "Call this script while in the root directory of the backend project";
|
||||||
|
exit 1;
|
||||||
|
fi;
|
||||||
|
|
||||||
|
git config --unset core.hooksPath
|
||||||
|
|
||||||
|
this_dir="$(dirname $(realpath "$0"))"
|
||||||
|
hook_script="$this_dir/pre-commit.sh"
|
||||||
|
ln -svf "$hook_script" "$this_dir/../.git/hooks/pre-commit"
|
||||||
|
|
||||||
|
echo "Commit hook installed"
|
39
gradle.yml
Normal file
39
gradle.yml
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
# vim: set ts=2 sw=2 et tw=80:
|
||||||
|
image: gradle:jdk13
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
- test
|
||||||
|
- deploy
|
||||||
|
|
||||||
|
smarthut_build:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- gradle assemble
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- build/libs/*.jar
|
||||||
|
expire_in: 1 week
|
||||||
|
|
||||||
|
smarthut_test:
|
||||||
|
stage: test
|
||||||
|
script:
|
||||||
|
- gradle check
|
||||||
|
|
||||||
|
smarthut_deploy:
|
||||||
|
stage: deploy
|
||||||
|
image: docker:latest
|
||||||
|
services:
|
||||||
|
- docker:dind
|
||||||
|
variables:
|
||||||
|
DOCKER_DRIVER: overlay
|
||||||
|
before_script:
|
||||||
|
- docker version
|
||||||
|
- docker info
|
||||||
|
- docker login -u smarthutsm -p $CI_DOCKER_PASS
|
||||||
|
script:
|
||||||
|
- "docker build -t smarthutsm/smarthut:${CI_COMMIT_BRANCH} --pull ."
|
||||||
|
- "docker push smarthutsm/smarthut:${CI_COMMIT_BRANCH}"
|
||||||
|
after_script:
|
||||||
|
- docker logout
|
||||||
|
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.2-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
172
gradlew
vendored
Executable file
172
gradlew
vendored
Executable file
|
@ -0,0 +1,172 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=$(save "$@")
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||||
|
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
84
gradlew.bat
vendored
Normal file
84
gradlew.bat
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS=
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
1
settings.gradle
Normal file
1
settings.gradle
Normal file
|
@ -0,0 +1 @@
|
||||||
|
rootProject.name = 'smarthut'
|
40
socket_test.html
Normal file
40
socket_test.html
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<!-- vim: set ts=2 sw=2 et tw=80: -->
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="malusa">
|
||||||
|
<h1>Waiting for authentication...</h1>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
let malusa = document.getElementById("malusa");
|
||||||
|
let connection = new WebSocket("ws://localhost:8080/sensor-socket");
|
||||||
|
console.log("***CREATED WEBSOCKET");
|
||||||
|
|
||||||
|
connection.onopen = function(evt) {
|
||||||
|
console.log("***ONOPEN", evt);
|
||||||
|
connection.send(JSON.stringify({token: prompt("insert authentication token")}));
|
||||||
|
};
|
||||||
|
|
||||||
|
connection.onmessage = function(evt) {
|
||||||
|
console.log("***ONMESSAGE", evt);
|
||||||
|
let data = JSON.parse(evt.data);
|
||||||
|
|
||||||
|
if (data.authenticated) {
|
||||||
|
malusa.innerHTML = "<h1>Socket is now authenticated!</h1>" +
|
||||||
|
"<img src='https://maggioni.xyz/astley.gif'>";
|
||||||
|
} else if (data.authenticated === false) {
|
||||||
|
malusa.innerHTML = "<h1>Authentication error</h1>";
|
||||||
|
} else {
|
||||||
|
malusa.innerHTML += "<p><pre>" + JSON.stringify(JSON.parse(evt.data), null, 2) + "</pre></p>";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
connection.onerror = function(evt) {
|
||||||
|
console.error("***ONERROR", evt);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,23 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.service;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.mail.SimpleMailMessage;
|
||||||
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service("emailSenderService")
|
||||||
|
public class EmailSenderService {
|
||||||
|
|
||||||
|
private JavaMailSender javaMailSender;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public EmailSenderService(JavaMailSender javaMailSender) {
|
||||||
|
this.javaMailSender = javaMailSender;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Async
|
||||||
|
public void sendEmail(SimpleMailMessage email) {
|
||||||
|
javaMailSender.send(email);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableScheduling
|
||||||
|
@EnableJpaRepositories("ch.usi.inf.sa4.sanmarinoes.smarthut.models")
|
||||||
|
public class SmarthutApplication {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(SmarthutApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import javax.servlet.*;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add CORS headers to each response in order to please the frontend requests, coming from a
|
||||||
|
* different host for now (thanks to the difference in ports). Andrea would you please stop
|
||||||
|
* complaining now
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class CORSFilter implements Filter {
|
||||||
|
|
||||||
|
public static void setCORSHeaders(HttpServletResponse response) {
|
||||||
|
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
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
|
||||||
|
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
final HttpServletResponse response = (HttpServletResponse) res;
|
||||||
|
|
||||||
|
setCORSHeaders(response);
|
||||||
|
|
||||||
|
chain.doFilter(req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(FilterConfig filterConfig) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {}
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to interface with `email.*` properties in application.properties. This properties are used
|
||||||
|
* for generating the email to send on password reset or registration
|
||||||
|
*
|
||||||
|
* @see ch.usi.inf.sa4.sanmarinoes.smarthut.controller.UserAccountController
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Validated
|
||||||
|
@EnableConfigurationProperties
|
||||||
|
@ConfigurationProperties(prefix = "email")
|
||||||
|
public class EmailConfigurationService {
|
||||||
|
|
||||||
|
/** The email subject for a registration email */
|
||||||
|
@NotNull private String registrationSubject;
|
||||||
|
|
||||||
|
/** The text in the email body preceding the confirmation URL for a registration email */
|
||||||
|
@NotNull private String registration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URL to follow for registration email confirmation. Has to end with the start of a query
|
||||||
|
* parameter
|
||||||
|
*/
|
||||||
|
@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;
|
||||||
|
|
||||||
|
@NotNull private String resetPasswordRedirect;
|
||||||
|
|
||||||
|
@NotNull private String registrationRedirect;
|
||||||
|
|
||||||
|
public String getRegistrationSubject() {
|
||||||
|
return registrationSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegistrationSubject(String registrationSubject) {
|
||||||
|
this.registrationSubject = registrationSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRegistration() {
|
||||||
|
return registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegistration(String registration) {
|
||||||
|
this.registration = registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRegistrationPath() {
|
||||||
|
return registrationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegistrationPath(String registrationPath) {
|
||||||
|
this.registrationPath = registrationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResetPasswordSubject() {
|
||||||
|
return resetPasswordSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResetPasswordSubject(String resetPasswordSubject) {
|
||||||
|
this.resetPasswordSubject = resetPasswordSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResetPassword() {
|
||||||
|
return resetPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResetPassword(String resetPassword) {
|
||||||
|
this.resetPassword = resetPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResetPasswordPath() {
|
||||||
|
return resetPasswordPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||||
|
|
||||||
|
import com.google.gson.*;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.converter.json.GsonHttpMessageConverter;
|
||||||
|
import springfox.documentation.spring.web.json.Json;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring configuration in order to register the GSON type adapter needed to avoid serializing twice
|
||||||
|
* Springfox Swagger JSON output (see: https://stackoverflow.com/a/30220562)
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class GsonConfig {
|
||||||
|
@Bean
|
||||||
|
public GsonHttpMessageConverter gsonHttpMessageConverter() {
|
||||||
|
GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
|
||||||
|
converter.setGson(gson());
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Gson gson() {
|
||||||
|
final GsonBuilder builder = new GsonBuilder();
|
||||||
|
builder.registerTypeAdapter(Json.class, new SpringfoxJsonToGsonAdapter());
|
||||||
|
builder.addSerializationExclusionStrategy(new AnnotationExclusionStrategy());
|
||||||
|
return builder.create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GSON type adapter needed to avoid serializing twice Springfox Swagger JSON output */
|
||||||
|
class SpringfoxJsonToGsonAdapter implements JsonSerializer<Json> {
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(Json json, Type type, JsonSerializationContext context) {
|
||||||
|
return JsonParser.parseString(json.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GSON exclusion strategy to exclude attributes with @GsonExclude */
|
||||||
|
class AnnotationExclusionStrategy implements ExclusionStrategy {
|
||||||
|
@Override
|
||||||
|
public boolean shouldSkipField(FieldAttributes f) {
|
||||||
|
return f.getAnnotation(GsonExclude.class) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldSkipClass(Class<?> clazz) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface GsonExclude {}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JWTAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void commence(
|
||||||
|
HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
AuthenticationException authException)
|
||||||
|
throws IOException {
|
||||||
|
if (!"OPTIONS".equals(request.getMethod())) {
|
||||||
|
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
|
||||||
|
} else {
|
||||||
|
CORSFilter.setCORSHeaders(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.JWTUserDetailsService;
|
||||||
|
import io.jsonwebtoken.ExpiredJwtException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JWTRequestFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
@Autowired private JWTUserDetailsService jwtUserDetailsService;
|
||||||
|
|
||||||
|
@Autowired private JWTTokenUtils jwtTokenUtils;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(
|
||||||
|
HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
final String requestTokenHeader = request.getHeader("Authorization");
|
||||||
|
String username = null;
|
||||||
|
String jwtToken = null;
|
||||||
|
|
||||||
|
// JWT Token is in the form "Bearer token". Remove Bearer word and get only the Token
|
||||||
|
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
|
||||||
|
jwtToken = requestTokenHeader.substring(7);
|
||||||
|
try {
|
||||||
|
username = jwtTokenUtils.getUsernameFromToken(jwtToken);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
System.out.println("Unable to get JWT Token");
|
||||||
|
} catch (ExpiredJwtException e) {
|
||||||
|
System.out.println("JWT Token has expired");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn("JWT Token does not begin with Bearer String");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once we get the token validate it.
|
||||||
|
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||||
|
UserDetails userDetails =
|
||||||
|
this.jwtUserDetailsService.loadUserByUsername(
|
||||||
|
username); // if token is valid configure Spring Security to manually
|
||||||
|
// set authentication
|
||||||
|
if (jwtTokenUtils.validateToken(jwtToken, userDetails)) {
|
||||||
|
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
|
||||||
|
new UsernamePasswordAuthenticationToken(
|
||||||
|
userDetails, null, userDetails.getAuthorities());
|
||||||
|
usernamePasswordAuthenticationToken.setDetails(
|
||||||
|
new WebAuthenticationDetailsSource().buildDetails(request));
|
||||||
|
// After setting the Authentication in the context, we specify
|
||||||
|
// that the current user is authenticated. So it passes the
|
||||||
|
// Spring Security Configurations successfully.
|
||||||
|
SecurityContextHolder.getContext()
|
||||||
|
.setAuthentication(usernamePasswordAuthenticationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.SignatureAlgorithm;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/** A utility class to handle JWTs */
|
||||||
|
@Component
|
||||||
|
public class JWTTokenUtils {
|
||||||
|
/** The duration in seconds of the validity of a single token */
|
||||||
|
private static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60;
|
||||||
|
|
||||||
|
/** The secret key used to encrypt all JWTs */
|
||||||
|
@Value("${jwt.secret}")
|
||||||
|
private String secret;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the claimed username from a given token
|
||||||
|
*
|
||||||
|
* @param token the token to inspect
|
||||||
|
* @return the username
|
||||||
|
*/
|
||||||
|
public String getUsernameFromToken(String token) {
|
||||||
|
return getClaimFromToken(token, Claims::getSubject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the token given is expired or not
|
||||||
|
*
|
||||||
|
* @param token the given token
|
||||||
|
* @return true if expired, false if not
|
||||||
|
*/
|
||||||
|
public Boolean isTokenExpired(String token) {
|
||||||
|
final Date expiration = getClaimFromToken(token, Claims::getExpiration);
|
||||||
|
return expiration.before(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new JWT for a given user. While creating the token - 1. Define claims of the token,
|
||||||
|
* like Issuer, Expiration, Subject, and the ID 2. Sign the JWT using the HS512 algorithm and
|
||||||
|
* secret key. 3. According to JWS Compact Serialization
|
||||||
|
* (https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1) compaction of
|
||||||
|
* the JWT to a URL-safe string
|
||||||
|
*
|
||||||
|
* @param user the user to which create a JWT
|
||||||
|
* @return the newly generated token
|
||||||
|
*/
|
||||||
|
public String generateToken(UserDetails user) {
|
||||||
|
return Jwts.builder()
|
||||||
|
.setClaims(new HashMap<>())
|
||||||
|
.setSubject(user.getUsername())
|
||||||
|
.setIssuedAt(new Date(System.currentTimeMillis()))
|
||||||
|
.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000))
|
||||||
|
.signWith(SignatureAlgorithm.HS512, secret)
|
||||||
|
.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the token given against matching userDetails
|
||||||
|
*
|
||||||
|
* @param token the token given
|
||||||
|
* @param userDetails user details to validate against
|
||||||
|
* @return true if valid, false if not
|
||||||
|
*/
|
||||||
|
public Boolean validateToken(String token, UserDetails userDetails) {
|
||||||
|
final String username = getUsernameFromToken(token);
|
||||||
|
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
|
||||||
|
final Claims claims = getAllClaimsFromToken(token);
|
||||||
|
return claimsResolver.apply(claims);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Claims getAllClaimsFromToken(String token) {
|
||||||
|
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import springfox.documentation.builders.ApiInfoBuilder;
|
||||||
|
import springfox.documentation.builders.PathSelectors;
|
||||||
|
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||||
|
import springfox.documentation.service.*;
|
||||||
|
import springfox.documentation.spi.DocumentationType;
|
||||||
|
import springfox.documentation.spi.service.contexts.SecurityContext;
|
||||||
|
import springfox.documentation.spring.web.plugins.Docket;
|
||||||
|
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class configures the automated REST documentation tool Swagger for this project. The
|
||||||
|
* documentation can be seen by going to http://localhost:8080/swaggeer-ui.html
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableSwagger2
|
||||||
|
@ComponentScan("ch.usi.inf.sa4.sanmarinoes.smarthut")
|
||||||
|
public class SpringFoxConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main definition of Springfox / swagger configuration
|
||||||
|
*
|
||||||
|
* @return a Docket object containing the swagger configuration
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public Docket api() {
|
||||||
|
return new Docket(DocumentationType.SWAGGER_2)
|
||||||
|
.select()
|
||||||
|
.apis(RequestHandlerSelectors.any())
|
||||||
|
.paths(paths()::test)
|
||||||
|
.build()
|
||||||
|
.apiInfo(apiInfo())
|
||||||
|
.securitySchemes(securitySchemes())
|
||||||
|
.securityContexts(List.of(securityContext()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the documentation about the smarthut authentication system
|
||||||
|
*
|
||||||
|
* @return a list of springfox authentication configurations
|
||||||
|
*/
|
||||||
|
private static List<? extends SecurityScheme> securitySchemes() {
|
||||||
|
return List.of(new ApiKey("Bearer", "Authorization", "header"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecurityContext securityContext() {
|
||||||
|
return SecurityContext.builder()
|
||||||
|
.securityReferences(defaultAuth())
|
||||||
|
.forPaths(authenticatedPaths()::test)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SecurityReference> defaultAuth() {
|
||||||
|
final AuthorizationScope authorizationScope =
|
||||||
|
new AuthorizationScope("global", "accessEverything");
|
||||||
|
return List.of(
|
||||||
|
new SecurityReference("Bearer", new AuthorizationScope[] {authorizationScope}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Predicate<String> authenticatedPaths() {
|
||||||
|
return ((Predicate<String>) PathSelectors.regex("/auth/update")::apply)
|
||||||
|
.or(PathSelectors.regex("/room.*")::apply)
|
||||||
|
.or(PathSelectors.regex("/device.*")::apply)
|
||||||
|
.or(PathSelectors.regex("/buttonDimmer.*")::apply)
|
||||||
|
.or(PathSelectors.regex("/dimmableLight.*")::apply)
|
||||||
|
.or(PathSelectors.regex("/knobDimmer.*")::apply)
|
||||||
|
.or(PathSelectors.regex("/regularLight.*")::apply)
|
||||||
|
.or(PathSelectors.regex("/sensor.*")::apply)
|
||||||
|
.or(PathSelectors.regex("/smartPlug.*")::apply)
|
||||||
|
.or(PathSelectors.regex("/switch.*")::apply)
|
||||||
|
.or(PathSelectors.regex("/motionSensor.*")::apply)
|
||||||
|
.or(PathSelectors.regex("/auth/profile.*")::apply);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the paths the documentation must be generated for. Add a path here only when the
|
||||||
|
* spec has been totally defined.
|
||||||
|
*
|
||||||
|
* @return A predicate that tests whether a path must be included or not
|
||||||
|
*/
|
||||||
|
private Predicate<String> paths() {
|
||||||
|
return PathSelectors.any()::apply;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the metadata about the smarthut project
|
||||||
|
*
|
||||||
|
* @return metadata about smarthut
|
||||||
|
*/
|
||||||
|
private ApiInfo apiInfo() {
|
||||||
|
return new ApiInfoBuilder()
|
||||||
|
.title("SmartHut.sm API")
|
||||||
|
.description("Backend API for the SanMariones version of the SA4 SmartHut project")
|
||||||
|
.termsOfServiceUrl("https://www.youtube.com/watch?v=9KxTcDsy9Gs")
|
||||||
|
.license("WTFPL")
|
||||||
|
.version("dev branch")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.JWTUserDetailsService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||||
|
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
@Autowired private JWTAuthenticationEntryPoint jwtAuthenticationEntryPoint;
|
||||||
|
@Autowired private JWTUserDetailsService jwtUserDetailsService;
|
||||||
|
@Autowired private JWTRequestFilter jwtRequestFilter;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
|
// configure AuthenticationManager so that it knows from where to load
|
||||||
|
// user for matching credentials
|
||||||
|
// Use BCryptPasswordEncoder
|
||||||
|
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Override
|
||||||
|
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||||
|
return super.authenticationManagerBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity httpSecurity) throws Exception {
|
||||||
|
// We don't need CSRF for this example
|
||||||
|
httpSecurity
|
||||||
|
.csrf()
|
||||||
|
.disable()
|
||||||
|
// dont authenticate this particular request
|
||||||
|
.authorizeRequests()
|
||||||
|
.antMatchers(
|
||||||
|
"/sensor-socket",
|
||||||
|
"/auth/login",
|
||||||
|
"/swagger-ui.html",
|
||||||
|
"/register",
|
||||||
|
"/register/confirm-account",
|
||||||
|
"/register/init-reset-password",
|
||||||
|
"/register/reset-password",
|
||||||
|
"/v2/api-docs",
|
||||||
|
"/webjars/**",
|
||||||
|
"/swagger-resources/**",
|
||||||
|
"/csrf")
|
||||||
|
.permitAll()
|
||||||
|
// all other requests need to be authenticated
|
||||||
|
.anyRequest()
|
||||||
|
.authenticated()
|
||||||
|
.and()
|
||||||
|
.
|
||||||
|
// make sure we use stateless session; session won't be used to
|
||||||
|
// store user's state.
|
||||||
|
exceptionHandling()
|
||||||
|
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
|
||||||
|
.and()
|
||||||
|
.sessionManagement()
|
||||||
|
.sessionCreationPolicy(
|
||||||
|
SessionCreationPolicy
|
||||||
|
.STATELESS); // Add a filter to validate the tokens with every
|
||||||
|
// request
|
||||||
|
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
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.error.UnauthorizedException;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UserNotFoundException;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||||
|
import java.security.Principal;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.security.authentication.DisabledException;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/auth")
|
||||||
|
public class AuthenticationController {
|
||||||
|
|
||||||
|
private final AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
|
private final JWTTokenUtils jwtTokenUtils;
|
||||||
|
|
||||||
|
private final JWTUserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
private BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||||
|
|
||||||
|
public AuthenticationController(
|
||||||
|
AuthenticationManager authenticationManager,
|
||||||
|
UserRepository userRepository,
|
||||||
|
JWTTokenUtils jwtTokenUtils,
|
||||||
|
JWTUserDetailsService userDetailsService) {
|
||||||
|
this.authenticationManager = authenticationManager;
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.jwtTokenUtils = jwtTokenUtils;
|
||||||
|
this.userDetailsService = userDetailsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/login")
|
||||||
|
public JWTResponse login(@Valid @RequestBody JWTRequest authenticationRequest)
|
||||||
|
throws UnauthorizedException, UserNotFoundException {
|
||||||
|
final UserDetails userDetails;
|
||||||
|
if (authenticationRequest.getUsernameOrEmail().contains("@")) {
|
||||||
|
// usernameOrEmail contains an email, so fetch the corresponding username
|
||||||
|
final User user =
|
||||||
|
userRepository.findByEmailIgnoreCase(
|
||||||
|
authenticationRequest.getUsernameOrEmail());
|
||||||
|
if (user == null) {
|
||||||
|
throw new UserNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticate(user.getUsername(), authenticationRequest.getPassword());
|
||||||
|
userDetails = userDetailsService.loadUserByUsername(user.getUsername());
|
||||||
|
} else {
|
||||||
|
// usernameOrEmail contains a username, authenticate with that then
|
||||||
|
authenticate(
|
||||||
|
authenticationRequest.getUsernameOrEmail(),
|
||||||
|
authenticationRequest.getPassword());
|
||||||
|
userDetails =
|
||||||
|
userDetailsService.loadUserByUsername(
|
||||||
|
authenticationRequest.getUsernameOrEmail());
|
||||||
|
}
|
||||||
|
|
||||||
|
final String token = jwtTokenUtils.generateToken(userDetails);
|
||||||
|
return new JWTResponse(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/profile")
|
||||||
|
public User profile(final Principal principal) {
|
||||||
|
return userRepository.findByUsername(principal.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void authenticate(String username, String password) throws UnauthorizedException {
|
||||||
|
try {
|
||||||
|
authenticationManager.authenticate(
|
||||||
|
new UsernamePasswordAuthenticationToken(username, password));
|
||||||
|
} catch (DisabledException e) {
|
||||||
|
throw new UnauthorizedException(true);
|
||||||
|
} catch (BadCredentialsException e) {
|
||||||
|
throw new UnauthorizedException(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
|
|
||||||
|
import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.ButtonDimmerDimRequest;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@RequestMapping("/buttonDimmer")
|
||||||
|
public class ButtonDimmerController
|
||||||
|
extends InputDeviceConnectionController<ButtonDimmer, DimmableLight> {
|
||||||
|
private ButtonDimmerRepository buttonDimmerRepository;
|
||||||
|
private DimmableLightRepository dimmableLightRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected ButtonDimmerController(
|
||||||
|
ButtonDimmerRepository inputRepository, DimmableLightRepository outputRepository) {
|
||||||
|
super(
|
||||||
|
inputRepository,
|
||||||
|
outputRepository,
|
||||||
|
DimmableLight.BUTTON_DIMMER_DIMMABLE_LIGHT_CONNECTOR);
|
||||||
|
this.buttonDimmerRepository = inputRepository;
|
||||||
|
this.dimmableLightRepository = outputRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<ButtonDimmer> findAll() {
|
||||||
|
return toList(buttonDimmerRepository.findAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ButtonDimmer findById(@PathVariable("id") long id) throws NotFoundException {
|
||||||
|
return buttonDimmerRepository.findById(id).orElseThrow(NotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ButtonDimmer create(@Valid @RequestBody final GenericDeviceSaveReguest bd) {
|
||||||
|
ButtonDimmer newBD = new ButtonDimmer();
|
||||||
|
newBD.setName(bd.getName());
|
||||||
|
newBD.setRoomId(bd.getRoomId());
|
||||||
|
|
||||||
|
return buttonDimmerRepository.save(newBD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/dim")
|
||||||
|
public Set<DimmableLight> dim(
|
||||||
|
@Valid @RequestBody final ButtonDimmerDimRequest bd, final Principal principal)
|
||||||
|
throws NotFoundException {
|
||||||
|
final ButtonDimmer buttonDimmer =
|
||||||
|
buttonDimmerRepository
|
||||||
|
.findByIdAndUsername(bd.getId(), principal.getName())
|
||||||
|
.orElseThrow(NotFoundException::new);
|
||||||
|
|
||||||
|
switch (bd.getDimType()) {
|
||||||
|
case UP:
|
||||||
|
buttonDimmer.increaseIntensity();
|
||||||
|
break;
|
||||||
|
case DOWN:
|
||||||
|
buttonDimmer.decreaseIntensity();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
dimmableLightRepository.saveAll(buttonDimmer.getOutputs());
|
||||||
|
|
||||||
|
return buttonDimmer.getOutputs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/lights")
|
||||||
|
public Set<? extends OutputDevice> addLight(
|
||||||
|
@PathVariable("id") long inputId, @RequestParam("lightId") Long lightId)
|
||||||
|
throws NotFoundException {
|
||||||
|
return addOutput(inputId, lightId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}/lights")
|
||||||
|
public Set<? extends OutputDevice> removeLight(
|
||||||
|
@PathVariable("id") long inputId, @RequestParam("lightId") Long lightId)
|
||||||
|
throws NotFoundException {
|
||||||
|
return removeOutput(inputId, lightId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public void delete(@PathVariable("id") long id) {
|
||||||
|
buttonDimmerRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.DeviceSaveRequest;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.BadDataException;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Device;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DeviceRepository;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RoomRepository;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@RequestMapping("/device")
|
||||||
|
public class DeviceController {
|
||||||
|
|
||||||
|
@Autowired private DeviceRepository<Device> deviceRepository;
|
||||||
|
@Autowired private RoomRepository roomRepository;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<Device> getAll(final Principal user) {
|
||||||
|
return deviceRepository.findAllByUsername(user.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping
|
||||||
|
public Device update(
|
||||||
|
@Valid @RequestBody DeviceSaveRequest deviceSaveRequest, final Principal principal)
|
||||||
|
throws NotFoundException, BadDataException {
|
||||||
|
final Device d =
|
||||||
|
deviceRepository
|
||||||
|
.findByIdAndUsername(deviceSaveRequest.getId(), principal.getName())
|
||||||
|
.orElseThrow(NotFoundException::new);
|
||||||
|
|
||||||
|
// check if roomId is valid
|
||||||
|
roomRepository
|
||||||
|
.findByIdAndUsername(deviceSaveRequest.getRoomId(), principal.getName())
|
||||||
|
.orElseThrow(() -> new BadDataException("roomId is not a valid room id"));
|
||||||
|
|
||||||
|
d.setRoomId(deviceSaveRequest.getRoomId());
|
||||||
|
d.setName(deviceSaveRequest.getName());
|
||||||
|
|
||||||
|
deviceRepository.save(d);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
|
|
||||||
|
import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.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;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@RequestMapping("/dimmableLight")
|
||||||
|
public class DimmableLightController {
|
||||||
|
|
||||||
|
@Autowired private DimmableLightRepository dimmableLightService;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<DimmableLight> findAll() {
|
||||||
|
return toList(dimmableLightService.findAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public DimmableLight findById(@PathVariable("id") long id) throws NotFoundException {
|
||||||
|
return dimmableLightService.findById(id).orElseThrow(NotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DimmableLight save(DimmableLight initial, DimmableLightSaveRequest dl) {
|
||||||
|
initial.setIntensity(dl.getIntensity());
|
||||||
|
initial.setName(dl.getName());
|
||||||
|
initial.setRoomId(dl.getRoomId());
|
||||||
|
|
||||||
|
return dimmableLightService.save(initial);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public DimmableLight create(@Valid @RequestBody DimmableLightSaveRequest dl) {
|
||||||
|
return save(new DimmableLight(), dl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping
|
||||||
|
public DimmableLight update(
|
||||||
|
@Valid @RequestBody DimmableLightSaveRequest sp, final Principal principal)
|
||||||
|
throws NotFoundException {
|
||||||
|
return save(
|
||||||
|
dimmableLightService
|
||||||
|
.findByIdAndUsername(sp.getId(), principal.getName())
|
||||||
|
.orElseThrow(NotFoundException::new),
|
||||||
|
sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public void delete(@PathVariable("id") long id) {
|
||||||
|
dimmableLightService.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract controller for an input device that has output connected to it. Aids to create the
|
||||||
|
* output add and output remove route
|
||||||
|
*
|
||||||
|
* @param <I> the type of device this controller is for
|
||||||
|
* @param <O> the output device attached to I
|
||||||
|
*/
|
||||||
|
public abstract class InputDeviceConnectionController<
|
||||||
|
I extends InputDevice, O extends OutputDevice> {
|
||||||
|
|
||||||
|
private class IOPair {
|
||||||
|
private final I input;
|
||||||
|
private final O output;
|
||||||
|
|
||||||
|
private IOPair(I input, O output) {
|
||||||
|
this.input = input;
|
||||||
|
this.output = output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeviceRepository<I> inputRepository;
|
||||||
|
|
||||||
|
private DeviceRepository<O> outputReposiory;
|
||||||
|
|
||||||
|
private Connector<I, O> connector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contstructs the controller by requiring essential object for the controller implementation
|
||||||
|
*
|
||||||
|
* @param inputRepository the input device repository
|
||||||
|
* @param outputRepository the output device repository
|
||||||
|
* @param connector a appropriate Connector instance for the I and O tyoes.
|
||||||
|
*/
|
||||||
|
protected InputDeviceConnectionController(
|
||||||
|
DeviceRepository<I> inputRepository,
|
||||||
|
DeviceRepository<O> outputRepository,
|
||||||
|
Connector<I, O> connector) {
|
||||||
|
this.inputRepository = inputRepository;
|
||||||
|
this.outputReposiory = outputRepository;
|
||||||
|
this.connector = connector;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IOPair checkConnectionIDs(Long inputId, Long outputId) throws NotFoundException {
|
||||||
|
final I input =
|
||||||
|
inputRepository
|
||||||
|
.findById(inputId)
|
||||||
|
.orElseThrow(() -> new NotFoundException("input device"));
|
||||||
|
final O output =
|
||||||
|
outputReposiory
|
||||||
|
.findById(outputId)
|
||||||
|
.orElseThrow(() -> new NotFoundException("output device"));
|
||||||
|
return new IOPair(input, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the output device connection creation (add) route
|
||||||
|
*
|
||||||
|
* @param inputId input device id
|
||||||
|
* @param outputId output device id
|
||||||
|
* @return the list of output devices attached to the input device of id inputId
|
||||||
|
* @throws NotFoundException if inputId or outputId are not valid
|
||||||
|
*/
|
||||||
|
protected Set<? extends OutputDevice> addOutput(Long inputId, Long outputId)
|
||||||
|
throws NotFoundException {
|
||||||
|
final IOPair pair = checkConnectionIDs(inputId, outputId);
|
||||||
|
connector.connect(pair.input, pair.output, true);
|
||||||
|
outputReposiory.save(pair.output);
|
||||||
|
return pair.input.getOutputs();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the output device connection destruction (remove) route
|
||||||
|
*
|
||||||
|
* @param inputId input device id
|
||||||
|
* @param outputId output device id
|
||||||
|
* @return the list of output devices attached to the input device of id inputId
|
||||||
|
* @throws NotFoundException if inputId or outputId are not valid
|
||||||
|
*/
|
||||||
|
protected Set<? extends OutputDevice> removeOutput(Long inputId, Long outputId)
|
||||||
|
throws NotFoundException {
|
||||||
|
final IOPair pair = checkConnectionIDs(inputId, outputId);
|
||||||
|
connector.connect(pair.input, pair.output, false);
|
||||||
|
outputReposiory.save(pair.output);
|
||||||
|
return pair.input.getOutputs();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
|
|
||||||
|
import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.KnobDimmerDimRequest;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@RequestMapping("/knobDimmer")
|
||||||
|
public class KnobDimmerController
|
||||||
|
extends InputDeviceConnectionController<KnobDimmer, DimmableLight> {
|
||||||
|
|
||||||
|
@Autowired private KnobDimmerRepository knobDimmerRepository;
|
||||||
|
@Autowired private DimmableLightRepository dimmableLightRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected KnobDimmerController(
|
||||||
|
KnobDimmerRepository inputRepository, DimmableLightRepository outputRepository) {
|
||||||
|
super(
|
||||||
|
inputRepository,
|
||||||
|
outputRepository,
|
||||||
|
DimmableLight.KNOB_DIMMER_DIMMABLE_LIGHT_CONNECTOR);
|
||||||
|
this.knobDimmerRepository = inputRepository;
|
||||||
|
this.dimmableLightRepository = outputRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<KnobDimmer> findAll() {
|
||||||
|
return toList(knobDimmerRepository.findAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public KnobDimmer findById(@PathVariable("id") long id) throws NotFoundException {
|
||||||
|
return knobDimmerRepository.findById(id).orElseThrow(NotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public KnobDimmer create(@Valid @RequestBody GenericDeviceSaveReguest kd) {
|
||||||
|
KnobDimmer newKD = new KnobDimmer();
|
||||||
|
newKD.setName(kd.getName());
|
||||||
|
newKD.setRoomId(kd.getRoomId());
|
||||||
|
|
||||||
|
return knobDimmerRepository.save(newKD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/dimTo")
|
||||||
|
public Set<DimmableLight> dimTo(
|
||||||
|
@Valid @RequestBody final KnobDimmerDimRequest bd, final Principal principal)
|
||||||
|
throws NotFoundException {
|
||||||
|
final KnobDimmer dimmer =
|
||||||
|
knobDimmerRepository
|
||||||
|
.findByIdAndUsername(bd.getId(), principal.getName())
|
||||||
|
.orElseThrow(NotFoundException::new);
|
||||||
|
|
||||||
|
dimmer.setLightIntensity(bd.getIntensity());
|
||||||
|
dimmableLightRepository.saveAll(dimmer.getOutputs());
|
||||||
|
|
||||||
|
return dimmer.getOutputs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/lights")
|
||||||
|
public Set<? extends OutputDevice> addLight(
|
||||||
|
@PathVariable("id") long inputId, @RequestParam("lightId") Long lightId)
|
||||||
|
throws NotFoundException {
|
||||||
|
return addOutput(inputId, lightId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}/lights")
|
||||||
|
public Set<? extends OutputDevice> removeLight(
|
||||||
|
@PathVariable("id") long inputId, @RequestParam("lightId") Long lightId)
|
||||||
|
throws NotFoundException {
|
||||||
|
return removeOutput(inputId, lightId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public void delete(@PathVariable("id") long id) {
|
||||||
|
knobDimmerRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
|
|
||||||
|
import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.MotionSensor;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.MotionSensorRepository;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@RequestMapping("/motionSensor")
|
||||||
|
public class MotionSensorController {
|
||||||
|
|
||||||
|
@Autowired private MotionSensorRepository motionSensorService;
|
||||||
|
|
||||||
|
@Autowired private SensorSocketEndpoint sensorSocketEndpoint;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<MotionSensor> findAll() {
|
||||||
|
return toList(motionSensorService.findAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public MotionSensor findById(@PathVariable("id") long id) throws NotFoundException {
|
||||||
|
return motionSensorService.findById(id).orElseThrow(NotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public MotionSensor create(@Valid @RequestBody GenericDeviceSaveReguest ms) {
|
||||||
|
MotionSensor newMS = new MotionSensor();
|
||||||
|
newMS.setName(ms.getName());
|
||||||
|
newMS.setRoomId(ms.getRoomId());
|
||||||
|
|
||||||
|
return motionSensorService.save(newMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates detection status of given motion sensor and propagates update throgh socket
|
||||||
|
*
|
||||||
|
* @param sensor the motion sensor to update
|
||||||
|
* @param detected the new detection status
|
||||||
|
* @return the updated motion sensor
|
||||||
|
*/
|
||||||
|
public MotionSensor updateDetectionFromMotionSensor(MotionSensor sensor, boolean detected) {
|
||||||
|
sensor.setDetected(detected);
|
||||||
|
final MotionSensor toReturn = motionSensorService.save(sensor);
|
||||||
|
|
||||||
|
sensorSocketEndpoint.broadcast(sensor, motionSensorService.findUser(sensor.getId()));
|
||||||
|
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}/detect")
|
||||||
|
public MotionSensor updateDetection(
|
||||||
|
@PathVariable("id") Long sensorId,
|
||||||
|
@RequestParam("detected") boolean detected,
|
||||||
|
final Principal principal)
|
||||||
|
throws NotFoundException {
|
||||||
|
|
||||||
|
return updateDetectionFromMotionSensor(
|
||||||
|
motionSensorService
|
||||||
|
.findByIdAndUsername(sensorId, principal.getName())
|
||||||
|
.orElseThrow(NotFoundException::new),
|
||||||
|
detected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public void delete(@PathVariable("id") long id) {
|
||||||
|
motionSensorService.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
|
|
||||||
|
import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.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;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@RequestMapping("/regularLight")
|
||||||
|
public class RegularLightController {
|
||||||
|
|
||||||
|
@Autowired private RegularLightRepository regularLightService;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<RegularLight> findAll() {
|
||||||
|
return toList(regularLightService.findAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public RegularLight findById(@PathVariable("id") long id) throws NotFoundException {
|
||||||
|
return regularLightService.findById(id).orElseThrow(NotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RegularLight save(RegularLight newRL, RegularLightSaveRequest rl) {
|
||||||
|
newRL.setName(rl.getName());
|
||||||
|
newRL.setRoomId(rl.getRoomId());
|
||||||
|
newRL.setOn(rl.isOn());
|
||||||
|
|
||||||
|
return regularLightService.save(newRL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public RegularLight create(@Valid @RequestBody RegularLightSaveRequest rl) {
|
||||||
|
return save(new RegularLight(), rl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping
|
||||||
|
public RegularLight update(
|
||||||
|
@Valid @RequestBody RegularLightSaveRequest rl, final Principal principal)
|
||||||
|
throws NotFoundException {
|
||||||
|
return save(
|
||||||
|
regularLightService
|
||||||
|
.findByIdAndUsername(rl.getId(), principal.getName())
|
||||||
|
.orElseThrow(NotFoundException::new),
|
||||||
|
rl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public void delete(@PathVariable("id") long id) {
|
||||||
|
regularLightService.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
|
|
||||||
|
import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.RoomSaveRequest;
|
||||||
|
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 javax.validation.Valid;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.*;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@RequestMapping("/room")
|
||||||
|
public class RoomController {
|
||||||
|
|
||||||
|
@Autowired private RoomRepository roomRepository;
|
||||||
|
|
||||||
|
@Autowired private UserRepository userRepository;
|
||||||
|
|
||||||
|
@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());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public @ResponseBody Room findById(@PathVariable("id") long id) throws NotFoundException {
|
||||||
|
return roomRepository.findById(id).orElseThrow(NotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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());
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return roomRepository.save(newRoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public void deleteById(@PathVariable("id") long id) {
|
||||||
|
switchRepository.deleteAllByRoomId(id);
|
||||||
|
knobDimmerRepository.deleteAllByRoomId(id);
|
||||||
|
buttonDimmerRepository.deleteAllByRoomId(id);
|
||||||
|
roomRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a List<Device> of all Devices that are present in a given room (identified by its
|
||||||
|
* id).
|
||||||
|
*/
|
||||||
|
@GetMapping(path = "/{roomId}/devices")
|
||||||
|
public List<Device> getDevices(@PathVariable("roomId") long roomid) {
|
||||||
|
return deviceRepository.findByRoomId(roomid);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
|
|
||||||
|
import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SensorSaveRequest;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.*;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@RequestMapping("/sensor")
|
||||||
|
public class SensorController {
|
||||||
|
|
||||||
|
@Autowired private SensorRepository sensorRepository;
|
||||||
|
|
||||||
|
@Autowired private SensorSocketEndpoint sensorSocketEndpoint;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<Sensor> findAll() {
|
||||||
|
return toList(sensorRepository.findAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Sensor findById(@PathVariable("id") long id) throws NotFoundException {
|
||||||
|
return sensorRepository.findById(id).orElseThrow(NotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public Sensor create(@Valid @RequestBody SensorSaveRequest s) {
|
||||||
|
Sensor newSensor = new Sensor();
|
||||||
|
newSensor.setSensor(s.getSensor());
|
||||||
|
newSensor.setName(s.getName());
|
||||||
|
newSensor.setRoomId(s.getRoomId());
|
||||||
|
newSensor.setValue(s.getValue());
|
||||||
|
|
||||||
|
return sensorRepository.save(newSensor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the sensor with new measurement and propagates update through websocket
|
||||||
|
*
|
||||||
|
* @param sensor the sensor to update
|
||||||
|
* @param value the new measurement
|
||||||
|
* @return the updated sensor
|
||||||
|
*/
|
||||||
|
public Sensor updateValueFromSensor(Sensor sensor, BigDecimal value) {
|
||||||
|
sensor.setValue(value);
|
||||||
|
final Sensor toReturn = sensorRepository.save(sensor);
|
||||||
|
|
||||||
|
sensorSocketEndpoint.broadcast(sensor, sensorRepository.findUser(sensor.getId()));
|
||||||
|
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}/value")
|
||||||
|
public Sensor updateValue(
|
||||||
|
@PathVariable("id") Long sensorId,
|
||||||
|
@RequestParam("value") BigDecimal value,
|
||||||
|
final Principal principal)
|
||||||
|
throws NotFoundException {
|
||||||
|
return updateValueFromSensor(
|
||||||
|
sensorRepository
|
||||||
|
.findByIdAndUsername(sensorId, principal.getName())
|
||||||
|
.orElseThrow(NotFoundException::new),
|
||||||
|
value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public void deleteById(@PathVariable("id") long id) {
|
||||||
|
sensorRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
|
|
||||||
|
import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.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;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.*;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@RequestMapping("/smartPlug")
|
||||||
|
public class SmartPlugController {
|
||||||
|
|
||||||
|
@Autowired private SmartPlugRepository smartPlugRepository;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<SmartPlug> findAll() {
|
||||||
|
return toList(smartPlugRepository.findAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public SmartPlug findById(@PathVariable("id") long id) throws NotFoundException {
|
||||||
|
return smartPlugRepository.findById(id).orElseThrow(NotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SmartPlug save(SmartPlug newSP, SmartPlugSaveRequest sp) {
|
||||||
|
newSP.setOn(sp.isOn());
|
||||||
|
newSP.setId(sp.getId());
|
||||||
|
newSP.setName(sp.getName());
|
||||||
|
newSP.setRoomId(sp.getRoomId());
|
||||||
|
|
||||||
|
return smartPlugRepository.save(newSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public SmartPlug create(@Valid @RequestBody SmartPlugSaveRequest sp) {
|
||||||
|
return save(new SmartPlug(), sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping
|
||||||
|
public SmartPlug update(@Valid @RequestBody SmartPlugSaveRequest sp, final Principal principal)
|
||||||
|
throws NotFoundException {
|
||||||
|
return save(
|
||||||
|
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}")
|
||||||
|
public void deleteById(@PathVariable("id") long id) {
|
||||||
|
smartPlugRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
|
|
||||||
|
import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SwitchOperationRequest;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.*;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@RequestMapping("/switch")
|
||||||
|
public class SwitchController extends InputDeviceConnectionController<Switch, Switchable> {
|
||||||
|
|
||||||
|
private SwitchRepository switchRepository;
|
||||||
|
private SwitchableRepository<Switchable> switchableRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contstructs the controller by requiring essential object for the controller implementation
|
||||||
|
*
|
||||||
|
* @param inputRepository the input device repository
|
||||||
|
* @param outputRepository the output device repository
|
||||||
|
*/
|
||||||
|
@Autowired
|
||||||
|
protected SwitchController(
|
||||||
|
SwitchRepository inputRepository, SwitchableRepository<Switchable> outputRepository) {
|
||||||
|
super(inputRepository, outputRepository, Switchable.SWITCH_SWITCHABLE_CONNECTOR);
|
||||||
|
this.switchRepository = inputRepository;
|
||||||
|
this.switchableRepository = outputRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<Switch> findAll() {
|
||||||
|
return toList(switchRepository.findAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Switch findById(@PathVariable("id") long id) throws NotFoundException {
|
||||||
|
return switchRepository.findById(id).orElseThrow(NotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public Switch create(@Valid @RequestBody GenericDeviceSaveReguest s) {
|
||||||
|
Switch newSwitch = new Switch();
|
||||||
|
newSwitch.setName(s.getName());
|
||||||
|
newSwitch.setRoomId(s.getRoomId());
|
||||||
|
|
||||||
|
return switchRepository.save(newSwitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/operate")
|
||||||
|
public Set<Switchable> operate(
|
||||||
|
@Valid @RequestBody final SwitchOperationRequest sr, final Principal principal)
|
||||||
|
throws NotFoundException {
|
||||||
|
final Switch s =
|
||||||
|
switchRepository
|
||||||
|
.findByIdAndUsername(sr.getId(), principal.getName())
|
||||||
|
.orElseThrow(NotFoundException::new);
|
||||||
|
|
||||||
|
switch (sr.getType()) {
|
||||||
|
case ON:
|
||||||
|
s.setOn(true);
|
||||||
|
break;
|
||||||
|
case OFF:
|
||||||
|
s.setOn(false);
|
||||||
|
break;
|
||||||
|
case TOGGLE:
|
||||||
|
s.toggle();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switchableRepository.saveAll(s.getOutputs());
|
||||||
|
|
||||||
|
return s.getOutputs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/lights")
|
||||||
|
public Set<? extends OutputDevice> addSwitchable(
|
||||||
|
@PathVariable("id") long inputId, @RequestParam("switchableId") Long switchableId)
|
||||||
|
throws NotFoundException {
|
||||||
|
return addOutput(inputId, switchableId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}/lights")
|
||||||
|
public Set<? extends OutputDevice> removeSwitchable(
|
||||||
|
@PathVariable("id") long inputId, @RequestParam("switchableId") Long switchableId)
|
||||||
|
throws NotFoundException {
|
||||||
|
return removeOutput(inputId, switchableId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public void deleteById(@PathVariable("id") long id) {
|
||||||
|
switchRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.EmailConfigurationService;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.InitPasswordResetRequest;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.PasswordResetRequest;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateRegistrationException;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.EmailTokenNotFoundException;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UserNotFoundException;
|
||||||
|
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 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;
|
||||||
|
import org.springframework.mail.SimpleMailMessage;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/** Unauthenticated set of endpoints to handle registration and password reset */
|
||||||
|
@RestController
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@RequestMapping("/register")
|
||||||
|
public class UserAccountController {
|
||||||
|
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
|
private final ConfirmationTokenRepository confirmationTokenRepository;
|
||||||
|
|
||||||
|
private final EmailSenderService emailSenderService;
|
||||||
|
|
||||||
|
private final BCryptPasswordEncoder encoder;
|
||||||
|
|
||||||
|
private final EmailConfigurationService emailConfig;
|
||||||
|
|
||||||
|
public UserAccountController(
|
||||||
|
UserRepository userRepository,
|
||||||
|
ConfirmationTokenRepository confirmationTokenRepository,
|
||||||
|
EmailSenderService emailSenderService,
|
||||||
|
BCryptPasswordEncoder encoder,
|
||||||
|
EmailConfigurationService emailConfig) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.confirmationTokenRepository = confirmationTokenRepository;
|
||||||
|
this.emailSenderService = emailSenderService;
|
||||||
|
this.encoder = encoder;
|
||||||
|
this.emailConfig = emailConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendEmail(String email, ConfirmationToken token, boolean isRegistration) {
|
||||||
|
SimpleMailMessage mailMessage = new SimpleMailMessage();
|
||||||
|
mailMessage.setTo(email);
|
||||||
|
mailMessage.setSubject(
|
||||||
|
isRegistration
|
||||||
|
? emailConfig.getRegistrationSubject()
|
||||||
|
: emailConfig.getResetPasswordSubject());
|
||||||
|
mailMessage.setFrom("smarthut.sm@gmail.com");
|
||||||
|
mailMessage.setText(
|
||||||
|
(isRegistration ? emailConfig.getRegistration() : emailConfig.getResetPassword())
|
||||||
|
+ " "
|
||||||
|
+ (isRegistration
|
||||||
|
? emailConfig.getRegistrationPath()
|
||||||
|
: emailConfig.getResetPasswordPath())
|
||||||
|
+ token.getConfirmationToken());
|
||||||
|
|
||||||
|
emailSenderService.sendEmail(mailMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unauthenticated endpoint to call to send a password reset email
|
||||||
|
*
|
||||||
|
* @param registrationData registration data of the new user
|
||||||
|
* @return success
|
||||||
|
* @throws DuplicateRegistrationException if a user exists with same email or username
|
||||||
|
*/
|
||||||
|
@PostMapping
|
||||||
|
public OkResponse registerUser(@Valid @RequestBody UserRegistrationRequest registrationData)
|
||||||
|
throws DuplicateRegistrationException {
|
||||||
|
final User existingEmailUser =
|
||||||
|
userRepository.findByEmailIgnoreCase(registrationData.getEmail());
|
||||||
|
final User existingUsernameUser =
|
||||||
|
userRepository.findByUsername(registrationData.getUsername());
|
||||||
|
|
||||||
|
// Check if an User with the same email already exists
|
||||||
|
if (existingEmailUser != null || existingUsernameUser != null) {
|
||||||
|
throw new DuplicateRegistrationException();
|
||||||
|
} else {
|
||||||
|
final User toSave = new User();
|
||||||
|
// disable the user (it will be enabled on email confiration)
|
||||||
|
toSave.setEnabled(false);
|
||||||
|
|
||||||
|
// encode user's password
|
||||||
|
toSave.setPassword(encoder.encode(registrationData.getPassword()));
|
||||||
|
|
||||||
|
// set other fields
|
||||||
|
toSave.setName(registrationData.getName());
|
||||||
|
toSave.setUsername(registrationData.getUsername());
|
||||||
|
toSave.setEmail(registrationData.getEmail());
|
||||||
|
userRepository.save(toSave);
|
||||||
|
|
||||||
|
ConfirmationToken token;
|
||||||
|
do {
|
||||||
|
token = new ConfirmationToken(toSave);
|
||||||
|
} while (confirmationTokenRepository.findByConfirmationToken(
|
||||||
|
token.getConfirmationToken())
|
||||||
|
!= null);
|
||||||
|
|
||||||
|
confirmationTokenRepository.save(token);
|
||||||
|
|
||||||
|
sendEmail(toSave.getEmail(), token, true);
|
||||||
|
|
||||||
|
return new OkResponse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unauthenticated endpoint to call to send a password reset email
|
||||||
|
*
|
||||||
|
* @param resetRequest a JSON object containing the email of the user to reset
|
||||||
|
* @return success
|
||||||
|
* @throws UserNotFoundException if given email does not belong to any user
|
||||||
|
*/
|
||||||
|
@PostMapping("/init-reset-password")
|
||||||
|
public OkResponse initResetPassword(@Valid @RequestBody InitPasswordResetRequest resetRequest)
|
||||||
|
throws UserNotFoundException {
|
||||||
|
final User toReset = userRepository.findByEmailIgnoreCase(resetRequest.getEmail());
|
||||||
|
|
||||||
|
// Check if an User with the same email already exists
|
||||||
|
if (toReset == null) {
|
||||||
|
throw new UserNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfirmationToken token;
|
||||||
|
do {
|
||||||
|
token = new ConfirmationToken(toReset);
|
||||||
|
token.setResetPassword(true);
|
||||||
|
} while (confirmationTokenRepository.findByConfirmationToken(token.getConfirmationToken())
|
||||||
|
!= null);
|
||||||
|
|
||||||
|
// Delete existing email password reset tokens
|
||||||
|
confirmationTokenRepository.deleteByUserAndResetPassword(toReset, true);
|
||||||
|
|
||||||
|
// Save new token
|
||||||
|
confirmationTokenRepository.save(token);
|
||||||
|
|
||||||
|
sendEmail(toReset.getEmail(), token, false);
|
||||||
|
|
||||||
|
return new OkResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unauthenticated endpoint to call with token sent by email to reset password
|
||||||
|
*
|
||||||
|
* @param resetRequest the token given via email and the new password
|
||||||
|
* @return success
|
||||||
|
* @throws EmailTokenNotFoundException if given token is not a valid token for password reset
|
||||||
|
*/
|
||||||
|
@PutMapping("/reset-password")
|
||||||
|
public OkResponse resetPassword(
|
||||||
|
@Valid @RequestBody PasswordResetRequest resetRequest,
|
||||||
|
final HttpServletResponse response)
|
||||||
|
throws EmailTokenNotFoundException, IOException {
|
||||||
|
final ConfirmationToken token =
|
||||||
|
confirmationTokenRepository.findByConfirmationToken(
|
||||||
|
resetRequest.getConfirmationToken());
|
||||||
|
|
||||||
|
if (token == null || !token.getResetPassword()) {
|
||||||
|
throw new EmailTokenNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
final User user = token.getUser();
|
||||||
|
user.setPassword(encoder.encode(resetRequest.getPassword()));
|
||||||
|
userRepository.save(user);
|
||||||
|
|
||||||
|
// Delete token to prevent further password changes
|
||||||
|
confirmationTokenRepository.delete(token);
|
||||||
|
|
||||||
|
return new OkResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unauthenticated endpoint to call with token sent by email to enable user
|
||||||
|
*
|
||||||
|
* @param confirmationToken the token given via email
|
||||||
|
* @return success
|
||||||
|
* @throws EmailTokenNotFoundException if given token is not a valid token for email
|
||||||
|
* confirmation
|
||||||
|
*/
|
||||||
|
@GetMapping(value = "/confirm-account")
|
||||||
|
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());
|
||||||
|
response.sendRedirect(emailConfig.getRegistrationRedirect());
|
||||||
|
} else {
|
||||||
|
throw new EmailTokenNotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/** A 'dim' event from a button dimmer. */
|
||||||
|
public class ButtonDimmerDimRequest {
|
||||||
|
|
||||||
|
/** The device id */
|
||||||
|
@NotNull private Long id;
|
||||||
|
|
||||||
|
public enum DimType {
|
||||||
|
UP,
|
||||||
|
DOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Whether the dim is up or down */
|
||||||
|
@NotNull private DimType dimType;
|
||||||
|
|
||||||
|
public DimType getDimType() {
|
||||||
|
return dimType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDimType(DimType dimType) {
|
||||||
|
this.dimType = dimType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class DeviceSaveRequest {
|
||||||
|
/** Device identifier */
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
|
||||||
|
* a REST call.
|
||||||
|
*/
|
||||||
|
@NotNull private Long roomId;
|
||||||
|
|
||||||
|
/** The name of the device as assigned by the user (e.g. 'Master bedroom light') */
|
||||||
|
@NotNull @NotEmpty private String name;
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getRoomId() {
|
||||||
|
return roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoomId(Long roomId) {
|
||||||
|
this.roomId = roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Max;
|
||||||
|
import javax.validation.constraints.Min;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class DimmableLightSaveRequest {
|
||||||
|
|
||||||
|
/** Device id (used only for update requests) */
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/** The light intensity value. Goes from 0 (off) to 100 (on) */
|
||||||
|
@NotNull
|
||||||
|
@Min(0)
|
||||||
|
@Max(100)
|
||||||
|
private Integer intensity = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
|
||||||
|
* a REST call.
|
||||||
|
*/
|
||||||
|
@NotNull private Long roomId;
|
||||||
|
|
||||||
|
/** The name of the device as assigned by the user (e.g. 'Master bedroom light') */
|
||||||
|
@NotNull private String name;
|
||||||
|
|
||||||
|
public void setRoomId(Long roomId) {
|
||||||
|
this.roomId = roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getRoomId() {
|
||||||
|
return roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getIntensity() {
|
||||||
|
return intensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIntensity(Integer intensity) {
|
||||||
|
this.intensity = intensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class GenericDeviceSaveReguest {
|
||||||
|
/**
|
||||||
|
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
|
||||||
|
* a REST call.
|
||||||
|
*/
|
||||||
|
@NotNull private Long roomId;
|
||||||
|
|
||||||
|
/** The name of the device as assigned by the user (e.g. 'Master bedroom light') */
|
||||||
|
@NotNull private String name;
|
||||||
|
|
||||||
|
public void setRoomId(Long roomId) {
|
||||||
|
this.roomId = roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getRoomId() {
|
||||||
|
return roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Email;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
|
|
||||||
|
/** DTO for password reset request */
|
||||||
|
public class InitPasswordResetRequest {
|
||||||
|
/**
|
||||||
|
* The user's email (validated according to criteria used in <code>>input type="email"<>
|
||||||
|
* </code>, technically not RFC 5322 compliant
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "Please provide an email")
|
||||||
|
@Email(message = "Please provide a valid email address")
|
||||||
|
@Pattern(regexp = ".+@.+\\..+", message = "Please provide a valid email address")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class JWTRequest {
|
||||||
|
@NotNull private String usernameOrEmail;
|
||||||
|
@NotNull private String password;
|
||||||
|
|
||||||
|
public String getUsernameOrEmail() {
|
||||||
|
return this.usernameOrEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsernameOrEmail(String usernameOrEmail) {
|
||||||
|
this.usernameOrEmail = usernameOrEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return this.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "JWTRequest{"
|
||||||
|
+ "usernameOrEmail='"
|
||||||
|
+ usernameOrEmail
|
||||||
|
+ '\''
|
||||||
|
+ ", password='"
|
||||||
|
+ password
|
||||||
|
+ '\''
|
||||||
|
+ '}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
public class JWTResponse {
|
||||||
|
private final String jwttoken;
|
||||||
|
|
||||||
|
public JWTResponse(String jwttoken) {
|
||||||
|
this.jwttoken = jwttoken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getToken() {
|
||||||
|
return this.jwttoken;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Max;
|
||||||
|
import javax.validation.constraints.Min;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class KnobDimmerDimRequest {
|
||||||
|
|
||||||
|
/** The device id */
|
||||||
|
@NotNull private Long id;
|
||||||
|
|
||||||
|
/** The absolute intensity value */
|
||||||
|
@NotNull
|
||||||
|
@Min(0)
|
||||||
|
@Max(100)
|
||||||
|
private Integer intensity;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getIntensity() {
|
||||||
|
return intensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIntensity(Integer intensity) {
|
||||||
|
this.intensity = intensity;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
/** A dummy DTO to return when there is no data to return */
|
||||||
|
public class OkResponse {
|
||||||
|
private boolean success = true;
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.*;
|
||||||
|
|
||||||
|
/** DTO for password reset request */
|
||||||
|
public class PasswordResetRequest {
|
||||||
|
|
||||||
|
@NotNull private String confirmationToken;
|
||||||
|
|
||||||
|
/** A properly salted way to store the password */
|
||||||
|
@NotNull
|
||||||
|
@NotEmpty(message = "Please provide a password")
|
||||||
|
@Size(
|
||||||
|
min = 6,
|
||||||
|
max = 255,
|
||||||
|
message = "Your password should be at least 6 characters long and up to 255 chars long")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
public String getConfirmationToken() {
|
||||||
|
return confirmationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConfirmationToken(String confirmationToken) {
|
||||||
|
this.confirmationToken = confirmationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class RegularLightSaveRequest {
|
||||||
|
/** The state of this switch */
|
||||||
|
private boolean on;
|
||||||
|
|
||||||
|
/** Device identifier */
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
|
||||||
|
* a REST call.
|
||||||
|
*/
|
||||||
|
@NotNull private Long roomId;
|
||||||
|
|
||||||
|
/** The name of the device as assigned by the user (e.g. 'Master bedroom light') */
|
||||||
|
@NotNull private String name;
|
||||||
|
|
||||||
|
public void setRoomId(Long roomId) {
|
||||||
|
this.roomId = roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getRoomId() {
|
||||||
|
return roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOn() {
|
||||||
|
return on;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOn(boolean on) {
|
||||||
|
this.on = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Room;
|
||||||
|
import javax.persistence.Lob;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class RoomSaveRequest {
|
||||||
|
|
||||||
|
/** Room identifier */
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
@NotNull private Room.Icon icon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image is to be given as byte[]. In order to get an encoded string from it, the
|
||||||
|
* Base64.getEncoder().encodeToString(byte[] content) should be used. For further information:
|
||||||
|
* https://www.baeldung.com/java-base64-image-string
|
||||||
|
* https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html
|
||||||
|
*/
|
||||||
|
@Lob private String image;
|
||||||
|
|
||||||
|
/** 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Room.Icon getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIcon(Room.Icon icon) {
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImage() {
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImage(String image) {
|
||||||
|
this.image = image;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Sensor;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class SensorSaveRequest {
|
||||||
|
|
||||||
|
/** Type of sensor, i.e. of the thing the sensor measures. */
|
||||||
|
public enum SensorType {
|
||||||
|
/** A sensor that measures temperature in degrees celsius */
|
||||||
|
@SerializedName("TEMPERATURE")
|
||||||
|
TEMPERATURE,
|
||||||
|
|
||||||
|
/** A sensor that measures relative humidity in percentage points */
|
||||||
|
@SerializedName("HUMIDITY")
|
||||||
|
HUMIDITY,
|
||||||
|
|
||||||
|
/** A sensor that measures light in degrees */
|
||||||
|
@SerializedName("LIGHT")
|
||||||
|
LIGHT
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The type of this sensor */
|
||||||
|
@NotNull
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
private Sensor.SensorType sensor;
|
||||||
|
|
||||||
|
@NotNull private BigDecimal value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
|
||||||
|
* a REST call.
|
||||||
|
*/
|
||||||
|
@NotNull private Long roomId;
|
||||||
|
|
||||||
|
/** The name of the device as assigned by the user (e.g. 'Master bedroom light') */
|
||||||
|
@NotNull private String name;
|
||||||
|
|
||||||
|
public void setRoomId(Long roomId) {
|
||||||
|
this.roomId = roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getRoomId() {
|
||||||
|
return roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sensor.SensorType getSensor() {
|
||||||
|
return sensor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSensor(Sensor.SensorType sensor) {
|
||||||
|
this.sensor = sensor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(BigDecimal value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class SmartPlugSaveRequest {
|
||||||
|
/** Whether the smart plug is on */
|
||||||
|
@NotNull private boolean on;
|
||||||
|
|
||||||
|
/** Device identifier */
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
|
||||||
|
* a REST call.
|
||||||
|
*/
|
||||||
|
@NotNull private Long roomId;
|
||||||
|
|
||||||
|
/** The name of the device as assigned by the user (e.g. 'Master bedroom light') */
|
||||||
|
@NotNull private String name;
|
||||||
|
|
||||||
|
public void setRoomId(Long roomId) {
|
||||||
|
this.roomId = roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getRoomId() {
|
||||||
|
return roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOn() {
|
||||||
|
return on;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOn(boolean on) {
|
||||||
|
this.on = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/** An on/off/toggle operation on a switch */
|
||||||
|
public class SwitchOperationRequest {
|
||||||
|
|
||||||
|
/** The device id */
|
||||||
|
@NotNull private Long id;
|
||||||
|
|
||||||
|
public enum OperationType {
|
||||||
|
ON,
|
||||||
|
OFF,
|
||||||
|
TOGGLE
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The type of switch operation */
|
||||||
|
@NotNull private SwitchOperationRequest.OperationType type;
|
||||||
|
|
||||||
|
public OperationType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(OperationType type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.*;
|
||||||
|
|
||||||
|
public class UserRegistrationRequest {
|
||||||
|
|
||||||
|
/** The full name of the user */
|
||||||
|
@NotNull
|
||||||
|
@NotEmpty(message = "Please provide a full name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/** The full name of the user */
|
||||||
|
@NotNull
|
||||||
|
@NotEmpty(message = "Please provide a username")
|
||||||
|
@Pattern(
|
||||||
|
regexp = "[A-Za-z0-9_\\-]+",
|
||||||
|
message = "Username can contain only letters, numbers, '_' and '-'")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/** A properly salted way to store the password */
|
||||||
|
@NotNull
|
||||||
|
@NotEmpty(message = "Please provide a password")
|
||||||
|
@Size(
|
||||||
|
min = 6,
|
||||||
|
max = 255,
|
||||||
|
message = "Your password should be at least 6 characters long and up to 255 chars long")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user's email (validated according to criteria used in <code>>input type="email"<>
|
||||||
|
* </code>, technically not RFC 5322 compliant
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
@NotEmpty(message = "Please provide an email")
|
||||||
|
@Email(message = "Please provide a valid email address")
|
||||||
|
@Pattern(regexp = ".+@.+\\..+", message = "Please provide a valid email address")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Email;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
|
|
||||||
|
public class UserUpdateRequest {
|
||||||
|
/** The full name of the user */
|
||||||
|
@NotEmpty(message = "Please provide a full name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/** A non-salted password */
|
||||||
|
@NotEmpty(message = "Please provide a password")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user's email (validated according to criteria used in <code>>input type="email"<>
|
||||||
|
* </code>, technically not RFC 5322 compliant
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "Please provide an email")
|
||||||
|
@Email(message = "Please provide a valid email address")
|
||||||
|
@Pattern(regexp = ".+@.+\\..+", message = "Please provide a valid email address")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.error;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
|
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
|
||||||
|
public class BadDataException extends Exception {
|
||||||
|
public BadDataException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.error;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
|
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
|
||||||
|
public class DuplicateRegistrationException extends Exception {
|
||||||
|
public DuplicateRegistrationException() {
|
||||||
|
super("Email or username already belonging to another user");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.error;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
|
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
|
||||||
|
public class EmailTokenNotFoundException extends Exception {
|
||||||
|
public EmailTokenNotFoundException() {
|
||||||
|
super("Email verification token not found in DB");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.error;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
|
@ResponseStatus(code = HttpStatus.NOT_FOUND)
|
||||||
|
public class NotFoundException extends Exception {
|
||||||
|
public NotFoundException() {
|
||||||
|
super("Not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotFoundException(String what) {
|
||||||
|
super(what + " not found");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.error;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
|
@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
|
||||||
|
public class UnauthorizedException extends Exception {
|
||||||
|
private final boolean isUserDisabled;
|
||||||
|
|
||||||
|
public UnauthorizedException(boolean isDisabled) {
|
||||||
|
super("Access denied: " + (isDisabled ? "user is disabled" : "wrong credentials"));
|
||||||
|
this.isUserDisabled = isDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUserDisabled() {
|
||||||
|
return isUserDisabled;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.error;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
|
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
|
||||||
|
public class UserNotFoundException extends Exception {
|
||||||
|
public UserNotFoundException() {
|
||||||
|
super("No user found with given email");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a dimmer that can only instruct an increase or decrease of intensity (i.e. like a
|
||||||
|
* dimmer with a '+' and a '-' button)
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
public class ButtonDimmer extends Dimmer {
|
||||||
|
|
||||||
|
/** The delta amount to apply to a increase or decrease intensity */
|
||||||
|
private static final int DIM_INCREMENT = 10;
|
||||||
|
|
||||||
|
public ButtonDimmer() {
|
||||||
|
super("buttonDimmer");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Increases the current intensity level of the dimmable light by DIM_INCREMENT */
|
||||||
|
public void increaseIntensity() {
|
||||||
|
for (DimmableLight dl : getOutputs()) {
|
||||||
|
dl.setIntensity(dl.getIntensity() + DIM_INCREMENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Decreases the current intensity level of the dimmable light by DIM_INCREMENT */
|
||||||
|
public void decreaseIntensity() {
|
||||||
|
for (DimmableLight dl : getOutputs()) {
|
||||||
|
dl.setIntensity(dl.getIntensity() - DIM_INCREMENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
|
public interface ButtonDimmerRepository extends DeviceRepository<ButtonDimmer> {
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
void deleteAllByRoomId(long roomId);
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.OneToOne;
|
||||||
|
import javax.persistence.Temporal;
|
||||||
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class ConfirmationToken {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
@Column(name = "id", updatable = false, nullable = false)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "confirmation_token", unique = true)
|
||||||
|
private String confirmationToken;
|
||||||
|
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
private Date createdDate;
|
||||||
|
|
||||||
|
@OneToOne(targetEntity = User.class, fetch = FetchType.EAGER)
|
||||||
|
@JoinColumn(nullable = false, name = "user_id")
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private Boolean resetPassword;
|
||||||
|
|
||||||
|
public ConfirmationToken(User user) {
|
||||||
|
this.user = user;
|
||||||
|
createdDate = new Date();
|
||||||
|
confirmationToken = UUID.randomUUID().toString();
|
||||||
|
resetPassword = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor for hibernate reflective stuff things whatever */
|
||||||
|
public ConfirmationToken() {}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getConfirmationToken() {
|
||||||
|
return confirmationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getCreatedDate() {
|
||||||
|
return createdDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConfirmationToken(String confirmationToken) {
|
||||||
|
this.confirmationToken = confirmationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreatedDate(Date createdDate) {
|
||||||
|
this.createdDate = createdDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUser(User user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getResetPassword() {
|
||||||
|
return resetPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResetPassword(Boolean resetPassword) {
|
||||||
|
this.resetPassword = resetPassword;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
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);
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A rule on how to connect an input device type to an output device type
|
||||||
|
*
|
||||||
|
* @param <I> the input device type
|
||||||
|
* @param <O> the output device type
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Connector<I extends InputDevice, O extends OutputDevice> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects or disconnects input to output
|
||||||
|
*
|
||||||
|
* @param input the input device
|
||||||
|
* @param output the output device
|
||||||
|
* @param connect true if connection, false if disconnection
|
||||||
|
*/
|
||||||
|
void connect(I input, O output, boolean connect);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces a basic implementation of a connector, assuming there is a OneToMany relationship
|
||||||
|
* between J and K
|
||||||
|
*
|
||||||
|
* @param outputsGetter the getter method of the set of outputs on the input class
|
||||||
|
* @param inputSetter the setter method for the input id on the output class
|
||||||
|
* @param <J> the input device type
|
||||||
|
* @param <K> the output device type
|
||||||
|
* @return a Connector implementation for the pair of types J and K
|
||||||
|
*/
|
||||||
|
static <J extends InputDevice, K extends OutputDevice> Connector<J, K> basic(
|
||||||
|
Function<J, Set<? super K>> outputsGetter, BiConsumer<K, Long> inputSetter) {
|
||||||
|
return (i, o, connect) -> {
|
||||||
|
if (connect) {
|
||||||
|
outputsGetter.apply(i).add(o);
|
||||||
|
} else {
|
||||||
|
outputsGetter.apply(i).remove(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
inputSetter.accept(o, connect ? i.getId() : null);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
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 javax.persistence.*;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/** Generic abstraction for a smart home device */
|
||||||
|
@Entity
|
||||||
|
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||||
|
public abstract class Device {
|
||||||
|
|
||||||
|
/** Ways a device can behave in the automation flow. For now only input/output */
|
||||||
|
public enum FlowType {
|
||||||
|
@SerializedName("INPUT")
|
||||||
|
INPUT,
|
||||||
|
|
||||||
|
@SerializedName("OUTPUT")
|
||||||
|
OUTPUT
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Device identifier */
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
@Column(name = "id", updatable = false, nullable = false, unique = true)
|
||||||
|
@ApiModelProperty(hidden = true)
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "room_id", updatable = false, insertable = false)
|
||||||
|
@GsonExclude
|
||||||
|
private Room room;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
@NotNull
|
||||||
|
private Long roomId;
|
||||||
|
|
||||||
|
/** The name of the device as assigned by the user (e.g. 'Master bedroom light') */
|
||||||
|
@NotNull
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name for the category of this particular device (e.g 'dimmer'). Not stored in the
|
||||||
|
* database but set thanks to constructors
|
||||||
|
*/
|
||||||
|
@Transient private final String kind;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The way this device behaves in the automation flow. Not stored in the database but set thanks
|
||||||
|
* to constructors
|
||||||
|
*/
|
||||||
|
@Transient private final FlowType flowType;
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getRoomId() {
|
||||||
|
return roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoomId(Long roomId) {
|
||||||
|
this.roomId = roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Device(String kind, FlowType flowType) {
|
||||||
|
this.kind = kind;
|
||||||
|
this.flowType = flowType;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DeviceRepository acts as a superclass for the other repositories so to mirror in the database the
|
||||||
|
* class inheritance present among the various devices.
|
||||||
|
*/
|
||||||
|
public interface DeviceRepository<T extends Device> extends CrudRepository<T, Long> {
|
||||||
|
List<T> findByRoomId(@Param("roomId") long roomId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds devices by their id and a username
|
||||||
|
*
|
||||||
|
* @param id the device id
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds all devices belonging to a user
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the user associated with a device through a room
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.validation.constraints.Max;
|
||||||
|
import javax.validation.constraints.Min;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/** Represent a dimmable light */
|
||||||
|
@Entity
|
||||||
|
public class DimmableLight extends Switchable {
|
||||||
|
|
||||||
|
public static final Connector<ButtonDimmer, DimmableLight>
|
||||||
|
BUTTON_DIMMER_DIMMABLE_LIGHT_CONNECTOR =
|
||||||
|
Connector.basic(ButtonDimmer::getOutputs, DimmableLight::setDimmerId);
|
||||||
|
|
||||||
|
public static final Connector<KnobDimmer, DimmableLight> KNOB_DIMMER_DIMMABLE_LIGHT_CONNECTOR =
|
||||||
|
Connector.basic(KnobDimmer::getOutputs, DimmableLight::setDimmerId);
|
||||||
|
|
||||||
|
public DimmableLight() {
|
||||||
|
super("dimmableLight");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@GsonExclude
|
||||||
|
@JoinColumn(name = "dimmer_id", updatable = false, insertable = false)
|
||||||
|
private Dimmer dimmer;
|
||||||
|
|
||||||
|
@Column(name = "dimmer_id")
|
||||||
|
private Long dimmerId;
|
||||||
|
|
||||||
|
/** The light intensity value. Goes from 0 (off) to 100 (on) */
|
||||||
|
@NotNull
|
||||||
|
@Column(nullable = false)
|
||||||
|
@Min(0)
|
||||||
|
@Max(100)
|
||||||
|
private Integer intensity = 0;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Column(nullable = false)
|
||||||
|
private Integer oldIntensity = 100;
|
||||||
|
|
||||||
|
public Integer getIntensity() {
|
||||||
|
return intensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the intensity to a certain level. Out of bound values are corrected to the respective
|
||||||
|
* extremums. An intensity level of 0 turns the light off, but keeps the old intensity level
|
||||||
|
* stored.
|
||||||
|
*
|
||||||
|
* @param intensity the intensity level (may be out of bounds)
|
||||||
|
*/
|
||||||
|
public void setIntensity(Integer intensity) {
|
||||||
|
if (intensity <= 0) {
|
||||||
|
this.intensity = 0;
|
||||||
|
} else if (intensity > 100) {
|
||||||
|
this.intensity = 100;
|
||||||
|
this.oldIntensity = 100;
|
||||||
|
} else {
|
||||||
|
this.intensity = intensity;
|
||||||
|
this.oldIntensity = intensity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOn() {
|
||||||
|
return intensity != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOn(boolean on) {
|
||||||
|
intensity = on ? oldIntensity : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDimmerId(Long dimmerId) {
|
||||||
|
this.dimmerId = dimmerId;
|
||||||
|
super.setSwitchId(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSwitchId(Long switchId) {
|
||||||
|
super.setSwitchId(switchId);
|
||||||
|
this.dimmerId = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
public interface DimmableLightRepository extends SwitchableRepository<DimmableLight> {}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
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
|
||||||
|
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||||
|
public abstract class Dimmer extends InputDevice {
|
||||||
|
public Dimmer(String kind) {
|
||||||
|
super(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "dimmer")
|
||||||
|
private Set<DimmableLight> lights = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the lights connected to this dimmer
|
||||||
|
*
|
||||||
|
* @return duh
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Set<DimmableLight> getOutputs() {
|
||||||
|
return this.lights;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add a light to be controller by this dimmer */
|
||||||
|
public void addDimmableLight(DimmableLight dimmableLight) {
|
||||||
|
lights.add(dimmableLight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreRemove
|
||||||
|
private void removeLightsFromDimmer() {
|
||||||
|
for (DimmableLight dl : getOutputs()) {
|
||||||
|
dl.setDimmerId(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Inheritance;
|
||||||
|
import javax.persistence.InheritanceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A generic abstraction for an input device, i.e. something that captures input either from the
|
||||||
|
* environment (sensor) or the user (switch / dimmer).
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||||
|
public abstract class InputDevice extends Device {
|
||||||
|
public InputDevice(String kind) {
|
||||||
|
super(kind, FlowType.INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<? extends OutputDevice> getOutputs() {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.core.*;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JWTUserDetailsService implements UserDetailsService {
|
||||||
|
@Autowired private UserRepository repository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
User toReturn = repository.findByUsername(username);
|
||||||
|
if (toReturn != null && toReturn.getEnabled()) {
|
||||||
|
return new org.springframework.security.core.userdetails.User(
|
||||||
|
toReturn.getUsername(), toReturn.getPassword(), Set.of());
|
||||||
|
} else {
|
||||||
|
throw new UsernameNotFoundException(username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a dimmer able to set absolute intensity values (i.e. knowing the absolute intensity
|
||||||
|
* value, like a knob)
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
public class KnobDimmer extends Dimmer {
|
||||||
|
|
||||||
|
public KnobDimmer() {
|
||||||
|
super("knobDimmer");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets absolutely the intensity level of all lights connected
|
||||||
|
*
|
||||||
|
* @param intensity the intensity (must be from 0 to 100)
|
||||||
|
*/
|
||||||
|
public void setLightIntensity(int intensity) {
|
||||||
|
for (DimmableLight dl : getOutputs()) {
|
||||||
|
dl.setIntensity(intensity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
|
public interface KnobDimmerRepository extends DeviceRepository<KnobDimmer> {
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
void deleteAllByRoomId(long roomId);
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
|
||||||
|
/** Represents a motion sensor device */
|
||||||
|
@Entity
|
||||||
|
public class MotionSensor extends InputDevice {
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private boolean detected;
|
||||||
|
|
||||||
|
public boolean isDetected() {
|
||||||
|
return detected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDetected(boolean detected) {
|
||||||
|
this.detected = detected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MotionSensor() {
|
||||||
|
super("motionSensor");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
public interface MotionSensorRepository extends DeviceRepository<MotionSensor> {}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a generic output device, i.e. something that causes some behaviour (light, smartPlugs,
|
||||||
|
* ...).
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||||
|
public abstract class OutputDevice extends Device {
|
||||||
|
public OutputDevice(String kind) {
|
||||||
|
super(kind, FlowType.OUTPUT);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/** Represents a standard non-dimmable light */
|
||||||
|
@Entity
|
||||||
|
public class RegularLight extends Switchable {
|
||||||
|
|
||||||
|
/** Whether the light is on or not */
|
||||||
|
@Column(name = "light_on", nullable = false)
|
||||||
|
@NotNull
|
||||||
|
boolean on;
|
||||||
|
|
||||||
|
public RegularLight() {
|
||||||
|
super("regularLight");
|
||||||
|
this.on = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOn() {
|
||||||
|
return on;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOn(boolean on) {
|
||||||
|
this.on = on;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
public interface RegularLightRepository extends SwitchableRepository<RegularLight> {}
|
|
@ -0,0 +1,203 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
/** Represents a room in the house owned by the user */
|
||||||
|
@Entity
|
||||||
|
public class Room {
|
||||||
|
|
||||||
|
/** A collection of Semantic UI icons */
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public enum Icon {
|
||||||
|
@SerializedName("home")
|
||||||
|
HOME("home"),
|
||||||
|
@SerializedName("coffee")
|
||||||
|
COFFEE("coffee"),
|
||||||
|
@SerializedName("beer")
|
||||||
|
BEER("beer"),
|
||||||
|
@SerializedName("glass martini")
|
||||||
|
GLASS_MARTINI("glass martini"),
|
||||||
|
@SerializedName("film")
|
||||||
|
FILM("film"),
|
||||||
|
@SerializedName("video")
|
||||||
|
VIDEO("video"),
|
||||||
|
@SerializedName("music")
|
||||||
|
MUSIC("music"),
|
||||||
|
@SerializedName("headphones")
|
||||||
|
HEADPHONES("headphones"),
|
||||||
|
@SerializedName("fax")
|
||||||
|
FAX("fax"),
|
||||||
|
@SerializedName("phone")
|
||||||
|
PHONE("phone"),
|
||||||
|
@SerializedName("laptop")
|
||||||
|
LAPTOP("laptop"),
|
||||||
|
@SerializedName("bath")
|
||||||
|
BATH("bath"),
|
||||||
|
@SerializedName("shower")
|
||||||
|
SHOWER("shower"),
|
||||||
|
@SerializedName("bed")
|
||||||
|
BED("bed"),
|
||||||
|
@SerializedName("child")
|
||||||
|
CHILD("child"),
|
||||||
|
@SerializedName("warehouse")
|
||||||
|
WAREHOUSE("warehouse"),
|
||||||
|
@SerializedName("car")
|
||||||
|
CAR("car"),
|
||||||
|
@SerializedName("bicycle")
|
||||||
|
BICYCLE("bicycle"),
|
||||||
|
@SerializedName("motorcycle")
|
||||||
|
MOTORCYCLE("motorcycle"),
|
||||||
|
@SerializedName("archive")
|
||||||
|
ARCHIVE("archive"),
|
||||||
|
@SerializedName("boxes")
|
||||||
|
BOXES("boxes"),
|
||||||
|
@SerializedName("cubes")
|
||||||
|
CUBES("cubes"),
|
||||||
|
@SerializedName("chess")
|
||||||
|
CHESS("chess"),
|
||||||
|
@SerializedName("gamepad")
|
||||||
|
GAMEPAD("gamepad"),
|
||||||
|
@SerializedName("futbol")
|
||||||
|
FUTBOL("futbol"),
|
||||||
|
@SerializedName("table tennis")
|
||||||
|
TABLE_TENNIS("table tennis"),
|
||||||
|
@SerializedName("server")
|
||||||
|
SERVER("server"),
|
||||||
|
@SerializedName("tv")
|
||||||
|
TV("tv"),
|
||||||
|
@SerializedName("heart")
|
||||||
|
HEART("heart"),
|
||||||
|
@SerializedName("camera")
|
||||||
|
CAMERA("camera"),
|
||||||
|
@SerializedName("trophy")
|
||||||
|
TROPHY("trophy"),
|
||||||
|
@SerializedName("wrench")
|
||||||
|
WRENCH("wrench"),
|
||||||
|
@SerializedName("image")
|
||||||
|
IMAGE("image"),
|
||||||
|
@SerializedName("book")
|
||||||
|
BOOK("book"),
|
||||||
|
@SerializedName("university")
|
||||||
|
UNIVERSITY("university"),
|
||||||
|
@SerializedName("medkit")
|
||||||
|
MEDKIT("medkit"),
|
||||||
|
@SerializedName("paw")
|
||||||
|
PAW("paw"),
|
||||||
|
@SerializedName("tree")
|
||||||
|
TREE("tree"),
|
||||||
|
@SerializedName("utensils")
|
||||||
|
UTENSILS("utensils"),
|
||||||
|
@SerializedName("male")
|
||||||
|
MALE("male"),
|
||||||
|
@SerializedName("female")
|
||||||
|
FEMALE("female"),
|
||||||
|
@SerializedName("life ring outline")
|
||||||
|
LIFE_RING_OUTLINE("life ring outline");
|
||||||
|
|
||||||
|
private String iconName;
|
||||||
|
|
||||||
|
Icon(String s) {
|
||||||
|
this.iconName = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return iconName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
@Column(name = "id", updatable = false, nullable = false, unique = true)
|
||||||
|
@ApiModelProperty(hidden = true)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/** The room icon, out of a set of Semantic UI icons */
|
||||||
|
@Column private Icon icon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image is to be given as byte[]. In order to get an encoded string from it, the
|
||||||
|
* Base64.getEncoder().encodeToString(byte[] content) should be used. For further information:
|
||||||
|
* https://www.baeldung.com/java-base64-image-string
|
||||||
|
* https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html
|
||||||
|
*/
|
||||||
|
@Column(name = "image", columnDefinition = "TEXT")
|
||||||
|
private String image;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "user_id", updatable = false, insertable = false)
|
||||||
|
@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)
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/** The user given name of this room (e.g. 'Master bedroom') */
|
||||||
|
@NotNull
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(Long userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Icon getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIcon(Icon icon) {
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImage() {
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImage(String image) {
|
||||||
|
this.image = image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Device> getDevices() {
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Room{" + "id=" + id + ", name='" + name + "\'}";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +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> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/** A sensor input device that measures a quantity in a continuous scale (e.g. temperature) */
|
||||||
|
@Entity
|
||||||
|
public class Sensor extends InputDevice {
|
||||||
|
|
||||||
|
public static final Map<SensorType, BigDecimal> TYPICAL_VALUES =
|
||||||
|
Map.of(
|
||||||
|
SensorType.TEMPERATURE, new BigDecimal(17.0),
|
||||||
|
SensorType.HUMIDITY, new BigDecimal(40.0),
|
||||||
|
SensorType.LIGHT, new BigDecimal(1000));
|
||||||
|
|
||||||
|
/** Type of sensor, i.e. of the thing the sensor measures. */
|
||||||
|
public enum SensorType {
|
||||||
|
/** A sensor that measures temperature in degrees celsius */
|
||||||
|
@SerializedName("TEMPERATURE")
|
||||||
|
TEMPERATURE,
|
||||||
|
|
||||||
|
/** A sensor that measures relative humidity in percentage points */
|
||||||
|
@SerializedName("HUMIDITY")
|
||||||
|
HUMIDITY,
|
||||||
|
|
||||||
|
/** A sensor that measures light in degrees */
|
||||||
|
@SerializedName("LIGHT")
|
||||||
|
LIGHT
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The value of this sensor according to its sensor type */
|
||||||
|
@Column(nullable = false, precision = 11, scale = 1)
|
||||||
|
private BigDecimal value;
|
||||||
|
|
||||||
|
/** The type of this sensor */
|
||||||
|
@Column(nullable = false)
|
||||||
|
@NotNull
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
private SensorType sensor;
|
||||||
|
|
||||||
|
public SensorType getSensor() {
|
||||||
|
return sensor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSensor(SensorType sensor) {
|
||||||
|
this.sensor = sensor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(BigDecimal newValue) {
|
||||||
|
this.value = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sensor() {
|
||||||
|
super("sensor");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Sensor{" + "value=" + value + ", sensor=" + sensor + '}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
public interface SensorRepository extends DeviceRepository<Sensor> {}
|
|
@ -0,0 +1,47 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
/** A smart plug that can be turned either on or off */
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOn(boolean on) {
|
||||||
|
this.on = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SmartPlug() {
|
||||||
|
super("smartPlug");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
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
|
||||||
|
public class Switch extends InputDevice {
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "switchDevice")
|
||||||
|
private Set<Switchable> switchables = new HashSet<>();
|
||||||
|
|
||||||
|
/** The state of this switch */
|
||||||
|
@Column(nullable = false, name = "switch_on")
|
||||||
|
private boolean on;
|
||||||
|
|
||||||
|
public Switch() {
|
||||||
|
super("switch");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setter method for this Switch
|
||||||
|
*
|
||||||
|
* @param state The state to be set
|
||||||
|
*/
|
||||||
|
public void setOn(boolean state) {
|
||||||
|
on = state;
|
||||||
|
|
||||||
|
for (final Switchable s : switchables) {
|
||||||
|
s.setOn(on);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Toggle between on and off state */
|
||||||
|
public void toggle() {
|
||||||
|
setOn(!isOn());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter method for this Switch
|
||||||
|
*
|
||||||
|
* @return This Switch on state
|
||||||
|
*/
|
||||||
|
public boolean isOn() {
|
||||||
|
return on;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Switchable> getOutputs() {
|
||||||
|
return switchables;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreRemove
|
||||||
|
public void removeSwitchable() {
|
||||||
|
for (Switchable s : getOutputs()) {
|
||||||
|
s.setSwitchId(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
|
public interface SwitchRepository extends DeviceRepository<Switch> {
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
void deleteAllByRoomId(long roomId);
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
|
||||||
|
import javax.persistence.*;
|
||||||
|
|
||||||
|
/** A device that can be turned either on or off */
|
||||||
|
@Entity
|
||||||
|
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||||
|
public abstract class Switchable extends OutputDevice {
|
||||||
|
|
||||||
|
public static final Connector<Switch, Switchable> SWITCH_SWITCHABLE_CONNECTOR =
|
||||||
|
Connector.basic(Switch::getOutputs, Switchable::setSwitchId);
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@GsonExclude
|
||||||
|
@JoinColumn(name = "switch_id", updatable = false, insertable = false)
|
||||||
|
private Switch switchDevice;
|
||||||
|
|
||||||
|
@Column(name = "switch_id")
|
||||||
|
private Long switchId;
|
||||||
|
|
||||||
|
protected Switchable(String kind) {
|
||||||
|
super(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the device is on (true) or not (false)
|
||||||
|
*
|
||||||
|
* @return whether the device is on (true) or not (false)
|
||||||
|
*/
|
||||||
|
public abstract boolean isOn();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the on status of the device
|
||||||
|
*
|
||||||
|
* @param on the new on status: true for on, false for off
|
||||||
|
*/
|
||||||
|
public abstract void setOn(boolean on);
|
||||||
|
|
||||||
|
public Long getSwitchId() {
|
||||||
|
return switchId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSwitchId(Long switchId) {
|
||||||
|
this.switchId = switchId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SwitchableRepository acts as a superclass for the other repositories so to mirror in the database
|
||||||
|
* the class inheritance present among the various switchable devices.
|
||||||
|
*/
|
||||||
|
public interface SwitchableRepository<T extends Switchable> extends DeviceRepository<T> {}
|
|
@ -0,0 +1,129 @@
|
||||||
|
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.*;
|
||||||
|
|
||||||
|
/** A user of the Smarthut application */
|
||||||
|
@Entity(name = "smarthutuser")
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
@Column(name = "id", updatable = false, nullable = false, unique = true)
|
||||||
|
@ApiModelProperty(hidden = true)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/** The full name of the user */
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/** The full username of the user */
|
||||||
|
@Column(nullable = false, unique = true)
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/** A properly salted way to store the password */
|
||||||
|
@Column(nullable = false)
|
||||||
|
@GsonExclude
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user's email (validated according to criteria used in <code>>input type="email"<>
|
||||||
|
* </code>, technically not RFC 5322 compliant
|
||||||
|
*/
|
||||||
|
@Column(nullable = false, unique = true)
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
@GsonExclude
|
||||||
|
private Boolean isEnabled = false;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getEnabled() {
|
||||||
|
return isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(Boolean enabled) {
|
||||||
|
isEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "User{"
|
||||||
|
+ "id="
|
||||||
|
+ id
|
||||||
|
+ ", name='"
|
||||||
|
+ name
|
||||||
|
+ '\''
|
||||||
|
+ ", username='"
|
||||||
|
+ username
|
||||||
|
+ '\''
|
||||||
|
+ ", password='"
|
||||||
|
+ password
|
||||||
|
+ '\''
|
||||||
|
+ ", email='"
|
||||||
|
+ email
|
||||||
|
+ '\''
|
||||||
|
+ ", isEnabled="
|
||||||
|
+ isEnabled
|
||||||
|
+ '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
User user = (User) o;
|
||||||
|
return id.equals(user.id)
|
||||||
|
&& name.equals(user.name)
|
||||||
|
&& username.equals(user.username)
|
||||||
|
&& password.equals(user.password)
|
||||||
|
&& email.equals(user.email)
|
||||||
|
&& isEnabled.equals(user.isEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(id, name, username, password, email, isEnabled);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
|
||||||
|
public interface UserRepository extends CrudRepository<User, Long> {
|
||||||
|
User findByUsername(String username);
|
||||||
|
|
||||||
|
User findByEmailIgnoreCase(String email);
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
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.*;
|
||||||
|
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;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates fake sensor (and motion sensor) and smart plug consumption updates as required by
|
||||||
|
* milestone one
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
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() {
|
||||||
|
StreamSupport.stream(sensorRepository.findAll().spliterator(), true)
|
||||||
|
.forEach(
|
||||||
|
sensor ->
|
||||||
|
sensorController.updateValueFromSensor(
|
||||||
|
sensor,
|
||||||
|
Sensor.TYPICAL_VALUES
|
||||||
|
.get(sensor.getSensor())
|
||||||
|
.multiply(
|
||||||
|
new BigDecimal(
|
||||||
|
0.9875 + Math.random() / 40))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate fake motion detections in all motion detectors every 20 seconds for 2 seconds at
|
||||||
|
* most
|
||||||
|
*/
|
||||||
|
@Scheduled(fixedDelay = 20000)
|
||||||
|
public void motionSensorFakeUpdate() {
|
||||||
|
StreamSupport.stream(motionSensorRepository.findAll().spliterator(), true)
|
||||||
|
.forEach(
|
||||||
|
sensor -> {
|
||||||
|
motionSensorController.updateDetectionFromMotionSensor(sensor, true);
|
||||||
|
CompletableFuture.delayedExecutor(
|
||||||
|
(long) (Math.random() * 2000), TimeUnit.MILLISECONDS)
|
||||||
|
.execute(
|
||||||
|
() ->
|
||||||
|
motionSensorController
|
||||||
|
.updateDetectionFromMotionSensor(
|
||||||
|
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())));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.socket;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonConfig;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.JWTTokenUtils;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import io.jsonwebtoken.ExpiredJwtException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import javax.websocket.MessageHandler;
|
||||||
|
import javax.websocket.Session;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/** Generates MessageHandlers for unauthenticated socket sessions */
|
||||||
|
@Component
|
||||||
|
public class AuthenticationMessageListener {
|
||||||
|
|
||||||
|
private Gson gson = GsonConfig.gson();
|
||||||
|
|
||||||
|
private JWTTokenUtils jwtTokenUtils;
|
||||||
|
|
||||||
|
private UserRepository userRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public AuthenticationMessageListener(
|
||||||
|
JWTTokenUtils jwtTokenUtils, UserRepository userRepository) {
|
||||||
|
this.jwtTokenUtils = jwtTokenUtils;
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new message handler to handle socket authentication
|
||||||
|
*
|
||||||
|
* @param session the session to which authentication must be checked
|
||||||
|
* @param authorizedSetter function to call once user is authenticated
|
||||||
|
* @return a new message handler to handle socket authentication
|
||||||
|
*/
|
||||||
|
MessageHandler.Whole<String> newHandler(
|
||||||
|
final Session session, BiConsumer<User, Session> authorizedSetter) {
|
||||||
|
return new MessageHandler.Whole<>() {
|
||||||
|
@Override
|
||||||
|
public void onMessage(final String message) {
|
||||||
|
if (message == null) {
|
||||||
|
acknowledge(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String token;
|
||||||
|
String username;
|
||||||
|
|
||||||
|
try {
|
||||||
|
token = gson.fromJson(message, JsonObject.class).get("token").getAsString();
|
||||||
|
username = jwtTokenUtils.getUsernameFromToken(token);
|
||||||
|
} catch (ExpiredJwtException e) {
|
||||||
|
System.err.println(e.getMessage());
|
||||||
|
acknowledge(false);
|
||||||
|
return;
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
System.out.println("Token format not valid");
|
||||||
|
acknowledge(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final User user = userRepository.findByUsername(username);
|
||||||
|
if (user == null || jwtTokenUtils.isTokenExpired(token)) {
|
||||||
|
System.out.println("Token not valid");
|
||||||
|
acknowledge(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here user is authenticated
|
||||||
|
session.removeMessageHandler(this);
|
||||||
|
|
||||||
|
// Add user-session pair in authorized list
|
||||||
|
authorizedSetter.accept(user, session);
|
||||||
|
|
||||||
|
// update client to acknowledge authentication
|
||||||
|
acknowledge(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void acknowledge(boolean success) {
|
||||||
|
try {
|
||||||
|
session.getBasicRemote()
|
||||||
|
.sendText(gson.toJson(Map.of("authenticated", success)));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.socket;
|
||||||
|
|
||||||
|
import javax.websocket.server.ServerEndpointConfig;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||||
|
import org.springframework.web.socket.server.standard.ServerEndpointRegistration;
|
||||||
|
|
||||||
|
/** Configures the sensor socket and maps it to the /sensor-socket path */
|
||||||
|
@Configuration
|
||||||
|
public class SensorSocketConfig extends ServerEndpointConfig.Configurator {
|
||||||
|
|
||||||
|
private SensorSocketEndpoint instance;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public SensorSocketConfig(SensorSocketEndpoint instance) {
|
||||||
|
this.instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the sensor socket endpoint to the url /sensor-socket
|
||||||
|
*
|
||||||
|
* @return an endpoint registration object
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public ServerEndpointRegistration serverEndpointRegistration() {
|
||||||
|
return new ServerEndpointRegistration("/sensor-socket", instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new ServerEndpointExporter
|
||||||
|
*
|
||||||
|
* @return a new ServerEndpointExporter
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public ServerEndpointExporter endpointExporter() {
|
||||||
|
return new ServerEndpointExporter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
|
||||||
|
try {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final T instance = (T) this.instance;
|
||||||
|
return instance;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
final var e2 =
|
||||||
|
new InstantiationException("Cannot cast SensorSocketEndpoint to desired type");
|
||||||
|
e2.initCause(e);
|
||||||
|
throw e2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.socket;
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/** Endpoint of socket at URL /sensor-socket used to update the client with sensor information */
|
||||||
|
@Component
|
||||||
|
public class SensorSocketEndpoint extends Endpoint {
|
||||||
|
|
||||||
|
private Gson gson = GsonConfig.gson();
|
||||||
|
|
||||||
|
private AuthenticationMessageListener authenticationMessageListener;
|
||||||
|
|
||||||
|
private Set<Session> unauthorizedClients = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
|
||||||
|
private Multimap<User, Session> authorizedClients =
|
||||||
|
Multimaps.synchronizedMultimap(HashMultimap.create());
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public SensorSocketEndpoint(AuthenticationMessageListener authenticationMessageListener) {
|
||||||
|
this.authenticationMessageListener = authenticationMessageListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a synchronized set of socket sessions not yet authorized with a token
|
||||||
|
*
|
||||||
|
* @return a synchronized set of socket sessions not yet authorized with a token
|
||||||
|
*/
|
||||||
|
public Set<Session> getUnauthorizedClients() {
|
||||||
|
return unauthorizedClients;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a synchronized User to Session multimap with authorized sessions
|
||||||
|
*
|
||||||
|
* @return a synchronized User to Session multimap with authorized sessions
|
||||||
|
*/
|
||||||
|
public Multimap<User, Session> getAuthorizedClients() {
|
||||||
|
return authorizedClients;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a message and a user, broadcasts that message in json to all associated clients and
|
||||||
|
* returns the number of successful transfers
|
||||||
|
*
|
||||||
|
* @param message the message to send
|
||||||
|
* @param u the user to which to send the message
|
||||||
|
* @return number of successful transfer
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the opening of a socket session with a client
|
||||||
|
*
|
||||||
|
* @param session the newly born session
|
||||||
|
* @param config endpoint configuration
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onOpen(Session session, EndpointConfig config) {
|
||||||
|
unauthorizedClients.add(session);
|
||||||
|
session.addMessageHandler(
|
||||||
|
authenticationMessageListener.newHandler(
|
||||||
|
session,
|
||||||
|
(u, s) -> {
|
||||||
|
unauthorizedClients.remove(s);
|
||||||
|
authorizedClients.put(u, s);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.utils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
/** A class with a bunch of useful static methods */
|
||||||
|
public final class Utils {
|
||||||
|
private Utils() {}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ConsumerWithException<T> {
|
||||||
|
void apply(T input) throws Throwable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> toList(Iterable<T> iterable) {
|
||||||
|
return StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Predicate<T> didThrow(ConsumerWithException<T> consumer) {
|
||||||
|
return (t) -> {
|
||||||
|
try {
|
||||||
|
consumer.apply(t);
|
||||||
|
return true;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
System.err.println(e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
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
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue