diff --git a/.gitignore b/.gitignore
index 1b1a367..ee01dda 100644
--- a/.gitignore
+++ b/.gitignore
@@ -136,3 +136,6 @@ gradle-app.setting
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
+
+# IntelliJ
+*.iml
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b618db2..2e3639a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -61,3 +61,4 @@ code_quality:
- build/reports/cpd/cpdCheck.xml
#create a report on the quality of the code
expose_as: 'Code Quality Report'
+ allow_failure: true
diff --git a/backend.iml b/backend.iml
deleted file mode 100644
index 1eae0df..0000000
--- a/backend.iml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index d4f0bd4..8ef658c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -20,6 +20,8 @@ dependencies {
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'
@@ -27,6 +29,7 @@ dependencies {
compile 'io.springfox:springfox-swagger2:2.9.2'
compile 'io.springfox:springfox-swagger-ui:2.9.2'
compile 'org.springframework.boot:spring-boot-configuration-processor'
+ testCompile 'org.springframework.boot:spring-boot-starter-webflux'
implementation('org.springframework.boot:spring-boot-starter-web') {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-json'
diff --git a/gradle.yml b/gradle.yml
new file mode 100644
index 0000000..51fefa4
--- /dev/null
+++ b/gradle.yml
@@ -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
+
diff --git a/socket_test.html b/socket_test.html
new file mode 100644
index 0000000..687388b
--- /dev/null
+++ b/socket_test.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
Waiting for authentication...
+
+
+
+
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmarthutApplication.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmarthutApplication.java
index 242f03f..57f7b42 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmarthutApplication.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmarthutApplication.java
@@ -3,8 +3,10 @@ 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) {
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java
index 3a2ab37..69c4fc9 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java
@@ -20,9 +20,10 @@ public class GsonConfig {
return converter;
}
- private Gson gson() {
+ public static Gson gson() {
final GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Json.class, new SpringfoxJsonToGsonAdapter());
+ builder.addSerializationExclusionStrategy(new AnnotationExclusionStrategy());
return builder.create();
}
}
@@ -34,3 +35,16 @@ class SpringfoxJsonToGsonAdapter implements JsonSerializer {
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;
+ }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonExclude.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonExclude.java
new file mode 100644
index 0000000..1be5551
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonExclude.java
@@ -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 {}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTRequestFilter.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTRequestFilter.java
index 853083b..e0cbb6a 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTRequestFilter.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTRequestFilter.java
@@ -20,7 +20,7 @@ public class JWTRequestFilter extends OncePerRequestFilter {
@Autowired private JWTUserDetailsService jwtUserDetailsService;
- @Autowired private JWTTokenUtil jwtTokenUtil;
+ @Autowired private JWTTokenUtils jwtTokenUtils;
@Override
protected void doFilterInternal(
@@ -30,13 +30,11 @@ public class JWTRequestFilter extends OncePerRequestFilter {
String username = null;
String jwtToken = null;
- // JWT Token is in th
- // e form "Bearer token". Remove Bearer word and get
- // only the Token
+ // 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 = jwtTokenUtil.getUsernameFromToken(jwtToken);
+ username = jwtTokenUtils.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
@@ -44,14 +42,15 @@ public class JWTRequestFilter extends OncePerRequestFilter {
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
- } // Once we get the token validate it.
+ }
+
+ // 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 (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
+ // set authentication
+ if (jwtTokenUtils.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtil.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtil.java
deleted file mode 100644
index 40d369f..0000000
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtil.java
+++ /dev/null
@@ -1,72 +0,0 @@
-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.Map;
-import java.util.function.Function;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.stereotype.Component;
-
-@Component
-public class JWTTokenUtil {
- public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60;
-
- @Value("${jwt.secret}")
- private String secret;
-
- // retrieve username from jwt token
- public String getUsernameFromToken(String token) {
- return getClaimFromToken(token, Claims::getSubject);
- }
-
- // retrieve expiration date from jwt token
- public Date getExpirationDateFromToken(String token) {
- return getClaimFromToken(token, Claims::getExpiration);
- }
-
- public T getClaimFromToken(String token, Function claimsResolver) {
- final Claims claims = getAllClaimsFromToken(token);
- return claimsResolver.apply(claims);
- }
-
- // for retrieveing any information from token we will need the secret key
- private Claims getAllClaimsFromToken(String token) {
- return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
- } // check if the token has expired
-
- private Boolean isTokenExpired(String token) {
- final Date expiration = getExpirationDateFromToken(token);
- return expiration.before(new Date());
- } // generate token for user
-
- public String generateToken(UserDetails userDetails) {
- Map claims = new HashMap<>();
- return doGenerateToken(claims, userDetails.getUsername());
- }
-
- // 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
- private String doGenerateToken(Map claims, String subject) {
- return Jwts.builder()
- .setClaims(claims)
- .setSubject(subject)
- .setIssuedAt(new Date(System.currentTimeMillis()))
- .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000))
- .signWith(SignatureAlgorithm.HS512, secret)
- .compact();
- }
-
- // validate token
- public Boolean validateToken(String token, UserDetails userDetails) {
- final String username = getUsernameFromToken(token);
- return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
- }
-}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtils.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtils.java
new file mode 100644
index 0000000..f6943a8
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtils.java
@@ -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 getClaimFromToken(String token, Function claimsResolver) {
+ final Claims claims = getAllClaimsFromToken(token);
+ return claimsResolver.apply(claims);
+ }
+
+ private Claims getAllClaimsFromToken(String token) {
+ return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
+ }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java
index 4011592..2fdab4e 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java
@@ -1,6 +1,5 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
-import static springfox.documentation.builders.PathSelectors.regex;
import java.util.List;
import java.util.function.Predicate;
@@ -10,10 +9,9 @@ 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.ApiInfo;
-import springfox.documentation.service.ApiKey;
-import springfox.documentation.service.SecurityScheme;
+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;
@@ -39,7 +37,8 @@ public class SpringFoxConfig {
.paths(paths()::test)
.build()
.apiInfo(apiInfo())
- .securitySchemes(securitySchemes());
+ .securitySchemes(securitySchemes())
+ .securityContexts(List.of(securityContext()));
}
/**
@@ -51,14 +50,32 @@ public class SpringFoxConfig {
return List.of(new ApiKey("Bearer", "Authorization", "header"));
}
- /**
- * Return a Java functional API predicate for regex matches
- *
- * @param regex the regex to match on
- * @return a Java functional API predicate
- */
- private Predicate regexPredicate(final String regex) {
- return regex(regex)::apply;
+ private SecurityContext securityContext() {
+ return SecurityContext.builder()
+ .securityReferences(defaultAuth())
+ .forPaths(authenticatedPaths()::test)
+ .build();
+ }
+
+ private List defaultAuth() {
+ final AuthorizationScope authorizationScope =
+ new AuthorizationScope("global", "accessEverything");
+ return List.of(
+ new SecurityReference("Bearer", new AuthorizationScope[] {authorizationScope}));
+ }
+
+ private Predicate authenticatedPaths() {
+ return ((Predicate) 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);
}
/**
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java
index 253998d..ec116c3 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java
@@ -51,6 +51,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// dont authenticate this particular request
.authorizeRequests()
.antMatchers(
+ "/sensor-socket",
"/auth/login",
"/swagger-ui.html",
"/register",
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java
index 1a1e266..3160e1c 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java
@@ -1,6 +1,6 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
-import ch.usi.inf.sa4.sanmarinoes.smarthut.config.JWTTokenUtil;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.config.JWTTokenUtils;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTResponse;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserUpdateRequest;
@@ -26,7 +26,7 @@ public class AuthenticationController {
private final UserRepository userRepository;
- private final JWTTokenUtil jwtTokenUtil;
+ private final JWTTokenUtils jwtTokenUtils;
private final JWTUserDetailsService userDetailsService;
@@ -35,11 +35,11 @@ public class AuthenticationController {
public AuthenticationController(
AuthenticationManager authenticationManager,
UserRepository userRepository,
- JWTTokenUtil jwtTokenUtil,
+ JWTTokenUtils jwtTokenUtils,
JWTUserDetailsService userDetailsService) {
this.authenticationManager = authenticationManager;
this.userRepository = userRepository;
- this.jwtTokenUtil = jwtTokenUtil;
+ this.jwtTokenUtils = jwtTokenUtils;
this.userDetailsService = userDetailsService;
}
@@ -68,7 +68,7 @@ public class AuthenticationController {
authenticationRequest.getUsernameOrEmail());
}
- final String token = jwtTokenUtil.generateToken(userDetails);
+ final String token = jwtTokenUtils.generateToken(userDetails);
return new JWTResponse(token);
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java
index 3d59036..061c6b9 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java
@@ -2,11 +2,12 @@ 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.ButtonDimmerSaveRequest;
+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.ButtonDimmer;
-import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ButtonDimmerRepository;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
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;
@@ -15,37 +16,77 @@ import org.springframework.web.bind.annotation.*;
@RestController
@EnableAutoConfiguration
@RequestMapping("/buttonDimmer")
-public class ButtonDimmerController {
- @Autowired private ButtonDimmerRepository buttonDimmerService;
+public class ButtonDimmerController
+ extends InputDeviceConnectionController {
+ 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 findAll() {
- return toList(buttonDimmerService.findAll());
+ return toList(buttonDimmerRepository.findAll());
}
@GetMapping("/{id}")
public ButtonDimmer findById(@PathVariable("id") long id) throws NotFoundException {
- return buttonDimmerService.findById(id).orElseThrow(NotFoundException::new);
+ return buttonDimmerRepository.findById(id).orElseThrow(NotFoundException::new);
}
@PostMapping
- public ButtonDimmer create(@Valid @RequestBody final ButtonDimmerSaveRequest bd) {
+ public ButtonDimmer create(@Valid @RequestBody final GenericDeviceSaveReguest bd) {
ButtonDimmer newBD = new ButtonDimmer();
- newBD.setLights(bd.getLights());
- newBD.setId(bd.getId());
newBD.setName(bd.getName());
newBD.setRoomId(bd.getRoomId());
- return buttonDimmerService.save(newBD);
+ return buttonDimmerRepository.save(newBD);
}
- @PutMapping
- public ButtonDimmer update(@Valid @RequestBody ButtonDimmerSaveRequest bd) {
- return this.create(bd);
+ @PutMapping("/dim")
+ public Set dim(@Valid @RequestBody final ButtonDimmerDimRequest bd)
+ throws NotFoundException {
+ final ButtonDimmer buttonDimmer =
+ buttonDimmerRepository.findById(bd.getId()).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) {
- buttonDimmerService.deleteById(id);
+ buttonDimmerRepository.deleteById(id);
}
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java
new file mode 100644
index 0000000..2d8dd99
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java
@@ -0,0 +1,48 @@
+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 deviceRepository;
+ @Autowired private RoomRepository roomRepository;
+
+ @GetMapping
+ public List getAll(final Principal user) {
+ return deviceRepository.findAllByUsername(user.getName());
+ }
+
+ @PutMapping
+ public Device update(@Valid @RequestBody DeviceSaveRequest deviceSaveRequest)
+ throws NotFoundException, BadDataException {
+ final Device d =
+ deviceRepository
+ .findById(deviceSaveRequest.getId())
+ .orElseThrow(NotFoundException::new);
+
+ // check if roomId is valid
+ roomRepository
+ .findById(deviceSaveRequest.getRoomId())
+ .orElseThrow(() -> new BadDataException("roomId is not a valid room id"));
+
+ d.setRoomId(deviceSaveRequest.getRoomId());
+ d.setName(deviceSaveRequest.getName());
+
+ deviceRepository.save(d);
+ return d;
+ }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java
index 3220b76..dd2e1e3 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java
@@ -29,20 +29,24 @@ public class DimmableLightController {
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) {
- DimmableLight newDL = new DimmableLight();
- newDL.setIntensity(dl.getIntensity());
- newDL.setId(dl.getId());
- newDL.setName(dl.getName());
- newDL.setRoomId(dl.getRoomId());
-
- return dimmableLightService.save(newDL);
+ return save(new DimmableLight(), dl);
}
@PutMapping
- public DimmableLight update(@Valid @RequestBody DimmableLightSaveRequest dl) {
- return this.create(dl);
+ public DimmableLight update(@Valid @RequestBody DimmableLightSaveRequest sp)
+ throws NotFoundException {
+ return save(
+ dimmableLightService.findById(sp.getId()).orElseThrow(NotFoundException::new), sp);
}
@DeleteMapping("/{id}")
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java
new file mode 100644
index 0000000..52f3483
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java
@@ -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 the type of device this controller is for
+ * @param 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 inputRepository;
+
+ private DeviceRepository outputReposiory;
+
+ private Connector 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 inputRepository,
+ DeviceRepository outputRepository,
+ Connector 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();
+ }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java
index 81f54ea..9f59889 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java
@@ -2,11 +2,12 @@ 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.KnobDimmerSaveRequest;
+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.KnobDimmer;
-import ch.usi.inf.sa4.sanmarinoes.smarthut.models.KnobDimmerRepository;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
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;
@@ -15,38 +16,70 @@ import org.springframework.web.bind.annotation.*;
@RestController
@EnableAutoConfiguration
@RequestMapping("/knobDimmer")
-public class KnobDimmerController {
+public class KnobDimmerController
+ extends InputDeviceConnectionController {
- @Autowired private KnobDimmerRepository knobDimmerService;
+ @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 findAll() {
- return toList(knobDimmerService.findAll());
+ return toList(knobDimmerRepository.findAll());
}
@GetMapping("/{id}")
public KnobDimmer findById(@PathVariable("id") long id) throws NotFoundException {
- return knobDimmerService.findById(id).orElseThrow(NotFoundException::new);
+ return knobDimmerRepository.findById(id).orElseThrow(NotFoundException::new);
}
@PostMapping
- public KnobDimmer create(@Valid @RequestBody KnobDimmerSaveRequest kd) {
+ public KnobDimmer create(@Valid @RequestBody GenericDeviceSaveReguest kd) {
KnobDimmer newKD = new KnobDimmer();
- newKD.setLights(kd.getLights());
- newKD.setId(kd.getId());
newKD.setName(kd.getName());
newKD.setRoomId(kd.getRoomId());
- return knobDimmerService.save(newKD);
+ return knobDimmerRepository.save(newKD);
}
- @PutMapping
- public KnobDimmer update(@Valid @RequestBody KnobDimmerSaveRequest kd) {
- return this.create(kd);
+ @PutMapping("/dimTo")
+ public Set dimTo(@Valid @RequestBody final KnobDimmerDimRequest bd)
+ throws NotFoundException {
+ final KnobDimmer dimmer =
+ knobDimmerRepository.findById(bd.getId()).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) {
- knobDimmerService.deleteById(id);
+ knobDimmerRepository.deleteById(id);
}
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java
index a09f900..59c0343 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java
@@ -2,10 +2,12 @@ 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.MotionSensorSaveRequest;
+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;
@@ -19,6 +21,8 @@ public class MotionSensorController {
@Autowired private MotionSensorRepository motionSensorService;
+ @Autowired private SensorSocketEndpoint sensorSocketEndpoint;
+
@GetMapping
public List findAll() {
return toList(motionSensorService.findAll());
@@ -30,19 +34,42 @@ public class MotionSensorController {
}
@PostMapping
- public MotionSensor create(@Valid @RequestBody MotionSensorSaveRequest ms) {
+ public MotionSensor create(@Valid @RequestBody GenericDeviceSaveReguest ms) {
MotionSensor newMS = new MotionSensor();
- newMS.setDetected(ms.isDetected());
- newMS.setId(ms.getId());
newMS.setName(ms.getName());
newMS.setRoomId(ms.getRoomId());
return motionSensorService.save(newMS);
}
- @PutMapping
- public MotionSensor update(@Valid @RequestBody MotionSensorSaveRequest ms) {
- return this.create(ms);
+ /**
+ * 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}")
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java
index 061f12c..15ce2e8 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java
@@ -36,10 +36,7 @@ public class RegularLightController {
return regularLightService.findById(id).orElseThrow(NotFoundException::new);
}
- @PostMapping
- public RegularLight create(@Valid @RequestBody RegularLightSaveRequest rl) {
- RegularLight newRL = new RegularLight();
- newRL.setId(rl.getId());
+ private RegularLight save(RegularLight newRL, RegularLightSaveRequest rl) {
newRL.setName(rl.getName());
newRL.setRoomId(rl.getRoomId());
newRL.setOn(rl.isOn());
@@ -47,9 +44,16 @@ public class RegularLightController {
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) {
- return this.create(rl);
+ public RegularLight update(@Valid @RequestBody RegularLightSaveRequest rl)
+ throws NotFoundException {
+ return save(
+ regularLightService.findById(rl.getId()).orElseThrow(NotFoundException::new), rl);
}
@DeleteMapping("/{id}")
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java
index 3a2ba78..fb42037 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java
@@ -39,17 +39,17 @@ public class RoomController {
final String username = principal.getName();
final Long userId = userRepository.findByUsername(username).getId();
final String img = r.getImage();
- final String icon = r.getIcon();
+ final Room.Icon icon = r.getIcon();
newRoom.setUserId(userId);
newRoom.setName(r.getName());
if (img != null) {
- newRoom.setImage(img.getBytes());
+ newRoom.setImage(img);
} else if (setWhenNull) {
newRoom.setImage(null);
}
if (icon != null) {
- newRoom.setIcon(icon.getBytes());
+ newRoom.setIcon(icon);
} else if (setWhenNull) {
newRoom.setIcon(null);
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java
index d738a37..ee1be81 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java
@@ -5,6 +5,9 @@ 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;
@@ -19,6 +22,8 @@ public class SensorController {
@Autowired private SensorRepository sensorRepository;
+ @Autowired private SensorSocketEndpoint sensorSocketEndpoint;
+
@GetMapping
public List findAll() {
return toList(sensorRepository.findAll());
@@ -33,17 +38,40 @@ public class SensorController {
public Sensor create(@Valid @RequestBody SensorSaveRequest s) {
Sensor newSensor = new Sensor();
newSensor.setSensor(s.getSensor());
- newSensor.setValue(s.getValue());
- newSensor.setId(s.getId());
newSensor.setName(s.getName());
newSensor.setRoomId(s.getRoomId());
+ newSensor.setValue(s.getValue());
return sensorRepository.save(newSensor);
}
- @PutMapping
- public Sensor update(@Valid @RequestBody SensorSaveRequest s) {
- return this.create(s);
+ /**
+ * 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}")
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java
index 43b66d9..8a57430 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java
@@ -29,9 +29,7 @@ public class SmartPlugController {
return smartPlugRepository.findById(id).orElseThrow(NotFoundException::new);
}
- @PostMapping
- public SmartPlug create(@Valid @RequestBody SmartPlugSaveRequest sp) {
- SmartPlug newSP = new SmartPlug();
+ private SmartPlug save(SmartPlug newSP, SmartPlugSaveRequest sp) {
newSP.setOn(sp.isOn());
newSP.setId(sp.getId());
newSP.setName(sp.getName());
@@ -40,9 +38,15 @@ public class SmartPlugController {
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) {
- return this.create(sp);
+ public SmartPlug update(@Valid @RequestBody SmartPlugSaveRequest sp) throws NotFoundException {
+ return save(
+ smartPlugRepository.findById(sp.getId()).orElseThrow(NotFoundException::new), sp);
}
@DeleteMapping("/{id}")
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java
index 5df72cf..f90108c 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java
@@ -2,7 +2,8 @@ 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.SwitchSaveRequest;
+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.util.*;
@@ -15,9 +16,24 @@ import org.springframework.web.bind.annotation.*;
@RestController
@EnableAutoConfiguration
@RequestMapping("/switch")
-public class SwitchController {
+public class SwitchController extends InputDeviceConnectionController {
- @Autowired private SwitchRepository switchRepository;
+ private SwitchRepository switchRepository;
+ private SwitchableRepository 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 outputRepository) {
+ super(inputRepository, outputRepository, Switchable.SWITCH_SWITCHABLE_CONNECTOR);
+ this.switchRepository = inputRepository;
+ this.switchableRepository = outputRepository;
+ }
@GetMapping
public List findAll() {
@@ -30,19 +46,48 @@ public class SwitchController {
}
@PostMapping
- public Switch create(@Valid @RequestBody SwitchSaveRequest s) {
+ public Switch create(@Valid @RequestBody GenericDeviceSaveReguest s) {
Switch newSwitch = new Switch();
- newSwitch.setId(s.getId());
newSwitch.setName(s.getName());
newSwitch.setRoomId(s.getRoomId());
- newSwitch.setOn(s.isOn());
return switchRepository.save(newSwitch);
}
- @PutMapping
- public Switch update(@Valid @RequestBody SwitchSaveRequest s) {
- return this.create(s);
+ @PutMapping("/operate")
+ public Set operate(@Valid @RequestBody final SwitchOperationRequest sr)
+ throws NotFoundException {
+ final Switch s = switchRepository.findById(sr.getId()).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}")
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/WelcomeController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/WelcomeController.java
deleted file mode 100644
index a81eec6..0000000
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/WelcomeController.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
-
-import org.springframework.boot.autoconfigure.*;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-
-@RestController
-@EnableAutoConfiguration
-public class WelcomeController {
-
- @GetMapping
- ResponseEntity testConnection() {
- return ResponseEntity.ok(null);
- }
-}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerDimRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerDimRequest.java
new file mode 100644
index 0000000..8e07015
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerDimRequest.java
@@ -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;
+ }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerSaveRequest.java
deleted file mode 100644
index 31a22d8..0000000
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerSaveRequest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
-
-import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableLight;
-import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Room;
-import java.util.HashSet;
-import java.util.Set;
-import javax.persistence.*;
-import javax.validation.constraints.NotNull;
-
-public class ButtonDimmerSaveRequest {
- @Lob private Set lights = new HashSet();
-
- /** Device identifier */
- private long id;
-
- /** The room this device belongs in */
- private Room room;
-
- /**
- * 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 Set getLights() {
- return this.lights;
- }
-
- public void setLights(Set newLights) {
- this.lights = newLights;
- }
-
- public void setRoom(Room room) {
- this.room = room;
- }
-
- public void setRoomId(Long roomId) {
- this.roomId = roomId;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public long getId() {
- return id;
- }
-
- public Room getRoom() {
- return room;
- }
-
- public Long getRoomId() {
- return roomId;
- }
-
- public String getName() {
- return name;
- }
-}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DeviceSaveRequest.java
similarity index 75%
rename from src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchSaveRequest.java
rename to src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DeviceSaveRequest.java
index c7516f2..a975117 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchSaveRequest.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DeviceSaveRequest.java
@@ -1,11 +1,9 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
+import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
-public class SwitchSaveRequest {
- /** The state of this switch */
- private boolean on;
-
+public class DeviceSaveRequest {
/** Device identifier */
private long id;
@@ -16,33 +14,29 @@ public class SwitchSaveRequest {
@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;
- }
+ @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 boolean isOn() {
- return on;
- }
-
- public void setOn(boolean on) {
- this.on = on;
+ public void setName(String name) {
+ this.name = name;
}
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DimmableLightSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DimmableLightSaveRequest.java
index 8edff94..01bec1a 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DimmableLightSaveRequest.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DimmableLightSaveRequest.java
@@ -1,24 +1,20 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
-import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Room;
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(1)
@Max(100)
private Integer intensity = 0;
- /** Device identifier */
- private long id;
-
- /** The room this device belongs in */
- private Room room;
-
/**
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
* a REST call.
@@ -28,10 +24,6 @@ public class DimmableLightSaveRequest {
/** The name of the device as assigned by the user (e.g. 'Master bedroom light') */
@NotNull private String name;
- public void setRoom(Room room) {
- this.room = room;
- }
-
public void setRoomId(Long roomId) {
this.roomId = roomId;
}
@@ -40,14 +32,6 @@ public class DimmableLightSaveRequest {
this.name = name;
}
- public long getId() {
- return id;
- }
-
- public Room getRoom() {
- return room;
- }
-
public Long getRoomId() {
return roomId;
}
@@ -60,10 +44,15 @@ public class DimmableLightSaveRequest {
return intensity;
}
- public void setIntensity(Integer intensity) throws IllegalArgumentException {
- if (intensity < 0 || intensity > 100) {
- throw new IllegalArgumentException("The intensity level can't go below 0 or above 100");
- }
+ public void setIntensity(Integer intensity) {
this.intensity = intensity;
}
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/MotionSensorSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/GenericDeviceSaveReguest.java
similarity index 66%
rename from src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/MotionSensorSaveRequest.java
rename to src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/GenericDeviceSaveReguest.java
index ba73495..8ec2671 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/MotionSensorSaveRequest.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/GenericDeviceSaveReguest.java
@@ -2,12 +2,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import javax.validation.constraints.NotNull;
-public class MotionSensorSaveRequest {
- private boolean detected;
-
- /** Device identifier */
- private long id;
-
+public class GenericDeviceSaveReguest {
/**
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
* a REST call.
@@ -25,10 +20,6 @@ public class MotionSensorSaveRequest {
this.name = name;
}
- public long getId() {
- return id;
- }
-
public Long getRoomId() {
return roomId;
}
@@ -36,12 +27,4 @@ public class MotionSensorSaveRequest {
public String getName() {
return name;
}
-
- public boolean isDetected() {
- return detected;
- }
-
- public void setDetected(boolean detected) {
- this.detected = detected;
- }
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerDimRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerDimRequest.java
new file mode 100644
index 0000000..6df303a
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerDimRequest.java
@@ -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;
+ }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerSaveRequest.java
deleted file mode 100644
index ce053e3..0000000
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerSaveRequest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
-
-import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableLight;
-import java.util.HashSet;
-import java.util.Set;
-import javax.persistence.Lob;
-import javax.validation.constraints.NotNull;
-
-public class KnobDimmerSaveRequest {
- @Lob private Set lights = new HashSet();
-
- /** 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 void setLights(Set lights) {
- this.lights = lights;
- }
-
- public Set getLights() {
- return lights;
- }
-}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RegularLightSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RegularLightSaveRequest.java
index ac1324d..99211e5 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RegularLightSaveRequest.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RegularLightSaveRequest.java
@@ -45,4 +45,8 @@ public class RegularLightSaveRequest {
public void setOn(boolean on) {
this.on = on;
}
+
+ public void setId(long id) {
+ this.id = id;
+ }
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RoomSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RoomSaveRequest.java
index f813b1c..ca8fa0f 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RoomSaveRequest.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RoomSaveRequest.java
@@ -1,17 +1,19 @@
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 {
+
+ @NotNull private Room.Icon icon;
+
/**
- * Icon and image are to be given as byte[]. In order to get an encoded string from it, the
+ * 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 icon;
-
@Lob private String image;
/** The user given name of this room (e.g. 'Master bedroom') */
@@ -25,11 +27,11 @@ public class RoomSaveRequest {
this.name = name;
}
- public String getIcon() {
+ public Room.Icon getIcon() {
return icon;
}
- public void setIcon(String icon) {
+ public void setIcon(Room.Icon icon) {
this.icon = icon;
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SensorSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SensorSaveRequest.java
index 421523c..62b0b5e 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SensorSaveRequest.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SensorSaveRequest.java
@@ -2,6 +2,7 @@ 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;
@@ -23,16 +24,12 @@ public class SensorSaveRequest {
LIGHT
}
- /** The value of this sensor according to its sensor type */
- private int value;
-
/** The type of this sensor */
@NotNull
@Enumerated(value = EnumType.STRING)
private Sensor.SensorType sensor;
- /** Device identifier */
- private long id;
+ @NotNull private BigDecimal value;
/**
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
@@ -51,10 +48,6 @@ public class SensorSaveRequest {
this.name = name;
}
- public long getId() {
- return id;
- }
-
public Long getRoomId() {
return roomId;
}
@@ -71,11 +64,11 @@ public class SensorSaveRequest {
this.sensor = sensor;
}
- public int getValue() {
- return this.value;
+ public BigDecimal getValue() {
+ return value;
}
- public void setValue(int newValue) {
- this.value = newValue;
+ public void setValue(BigDecimal value) {
+ this.value = value;
}
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SmartPlugSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SmartPlugSaveRequest.java
index 3318505..6b2f9b5 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SmartPlugSaveRequest.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SmartPlugSaveRequest.java
@@ -45,4 +45,8 @@ public class SmartPlugSaveRequest {
public void setOn(boolean on) {
this.on = on;
}
+
+ public void setId(long id) {
+ this.id = id;
+ }
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchOperationRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchOperationRequest.java
new file mode 100644
index 0000000..3fb552b
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchOperationRequest.java
@@ -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;
+ }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/BadDataException.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/BadDataException.java
new file mode 100644
index 0000000..2c6c4d4
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/BadDataException.java
@@ -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);
+ }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/NotFoundException.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/NotFoundException.java
index 1d5f90d..471107f 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/NotFoundException.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/NotFoundException.java
@@ -6,6 +6,10 @@ import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(code = HttpStatus.NOT_FOUND)
public class NotFoundException extends Exception {
public NotFoundException() {
- super("Not Found");
+ super("Not found");
+ }
+
+ public NotFoundException(String what) {
+ super(what + " not found");
}
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java
index 1e5f702..fc91c22 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java
@@ -1,9 +1,6 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
-
-import java.util.Set;
import javax.persistence.Entity;
-import javax.persistence.OneToMany;
/**
* Represents a dimmer that can only instruct an increase or decrease of intensity (i.e. like a
@@ -11,60 +8,25 @@ import javax.persistence.OneToMany;
*/
@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("button-dimmer");
}
- @OneToMany(mappedBy = "dimmer")
- private Set lights;
-
- /** Increases the current intensity level of the dimmable light by 1 */
+ /** Increases the current intensity level of the dimmable light by DIM_INCREMENT */
public void increaseIntensity() {
- for (DimmableLight dl : lights) {
- dl.setIntensity(dl.getIntensity() + 1);
+ for (DimmableLight dl : getOutputs()) {
+ dl.setIntensity(dl.getIntensity() + DIM_INCREMENT);
}
}
- /** Decreases the current intensity level of the dimmable light by 1 */
+ /** Decreases the current intensity level of the dimmable light by DIM_INCREMENT */
public void decreaseIntensity() {
- for (DimmableLight dl : lights) {
- dl.setIntensity(dl.getIntensity() - 1);
+ for (DimmableLight dl : getOutputs()) {
+ dl.setIntensity(dl.getIntensity() - DIM_INCREMENT);
}
}
-
- /**
- * Adds a DimmableLight to this set of DimmableLights
- *
- * @param dl The DimmableLight to be added
- */
- public void addLight(DimmableLight dl) {
- lights.add(dl);
- }
-
- /**
- * Removes the given DimmableLight
- *
- * @param dl The DimmableLight to be removed
- */
- public void removeLight(DimmableLight dl) {
- lights.remove(dl);
- }
-
- /** Clears this set */
- public void clearSet() {
- lights.clear();
- }
-
- /**
- * Get the lights
- *
- * @return duh
- */
- public Set getLights() {
- return this.lights;
- }
-
- public void setLights(Set newLights) {
- this.lights = newLights;
- }
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationTokenRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationTokenRepository.java
index 9bf3791..40c6a17 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationTokenRepository.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationTokenRepository.java
@@ -6,6 +6,8 @@ import org.springframework.data.repository.CrudRepository;
public interface ConfirmationTokenRepository extends CrudRepository {
ConfirmationToken findByConfirmationToken(String confirmationToken);
+ ConfirmationToken findByUser(User user);
+
@Transactional
void deleteByUserAndResetPassword(User user, boolean resetPassword);
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connector.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connector.java
new file mode 100644
index 0000000..91b1e88
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connector.java
@@ -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 the input device type
+ * @param the output device type
+ */
+@FunctionalInterface
+public interface Connector {
+
+ /**
+ * 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 the input device type
+ * @param the output device type
+ * @return a Connector implementation for the pair of types J and K
+ */
+ static Connector basic(
+ Function> outputsGetter, BiConsumer 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);
+ };
+ }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java
index 7aa65e8..6d0333d 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java
@@ -1,5 +1,6 @@
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.*;
@@ -26,11 +27,16 @@ public abstract class Device {
@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)
+ @Column(name = "room_id", nullable = false, unique = true)
@NotNull
private Long roomId;
@@ -43,17 +49,13 @@ public abstract class Device {
* The name for the category of this particular device (e.g 'dimmer'). Not stored in the
* database but set thanks to constructors
*/
- @ApiModelProperty(hidden = true)
- @Transient
- private final String kind;
+ @Transient private final String kind;
/**
* The way this device behaves in the automation flow. Not stored in the database but set thanks
* to constructors
*/
- @ApiModelProperty(hidden = true)
- @Transient
- private final FlowType flowType;
+ @Transient private final FlowType flowType;
public long getId() {
return id;
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DeviceRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DeviceRepository.java
index fdae66e..f844882 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DeviceRepository.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DeviceRepository.java
@@ -1,6 +1,8 @@
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;
@@ -10,4 +12,32 @@ import org.springframework.data.repository.query.Param;
*/
public interface DeviceRepository extends CrudRepository {
List 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
+ */
+ @Query("SELECT d FROM Device d JOIN d.room r JOIN r.user u WHERE d.id = ?1 AND u.username = ?2")
+ Optional findByIdAndUsername(Long id, String username);
+
+ /**
+ * Finds all devices belonging to a user
+ *
+ * @param username the User's username
+ * @return all devices of that user
+ */
+ @Query("SELECT d FROM Device d JOIN d.room r JOIN r.user u WHERE u.username = ?1")
+ List findAllByUsername(String username);
+
+ /**
+ * Find the user associated with a device through a room
+ *
+ * @param deviceId the device id
+ * @return a user object
+ */
+ @Query("SELECT u FROM Device d JOIN d.room r JOIN r.user u WHERE d.id = ?1")
+ User findUser(Long deviceId);
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java
index a71afbc..9a75471 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java
@@ -1,7 +1,9 @@
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;
@@ -9,18 +11,31 @@ import javax.validation.constraints.NotNull;
/** Represent a dimmable light */
@Entity
-public class DimmableLight extends Light {
+public class DimmableLight extends Switchable {
+
+ public static final Connector
+ BUTTON_DIMMER_DIMMABLE_LIGHT_CONNECTOR =
+ Connector.basic(ButtonDimmer::getOutputs, DimmableLight::setDimmerId);
+
+ public static final Connector KNOB_DIMMER_DIMMABLE_LIGHT_CONNECTOR =
+ Connector.basic(KnobDimmer::getOutputs, DimmableLight::setDimmerId);
public DimmableLight() {
super("light");
}
- @ManyToOne private Dimmer dimmer;
+ @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(1)
+ @Min(0)
@Max(100)
private Integer intensity = 0;
@@ -28,10 +43,41 @@ public class DimmableLight extends Light {
return intensity;
}
- public void setIntensity(Integer intensity) throws IllegalArgumentException {
- if (intensity < 0 || intensity > 100) {
- throw new IllegalArgumentException("The intensity level can't go below 0 or above 100");
+ /**
+ * 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;
+ } else {
+ this.intensity = intensity;
}
- this.intensity = intensity;
+ }
+
+ @Override
+ public boolean isOn() {
+ return intensity != 0;
+ }
+
+ @Override
+ public void setOn(boolean on) {
+ intensity = on ? 100 : 0;
+ }
+
+ public void setDimmerId(Long dimmerId) {
+ this.dimmerId = dimmerId;
+ super.setSwitchId(null);
+ };
+
+ @Override
+ public void setSwitchId(Long switchId) {
+ super.setSwitchId(switchId);
+ this.dimmerId = null;
}
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLightRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLightRepository.java
index 484084b..a32b3c6 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLightRepository.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLightRepository.java
@@ -1,3 +1,3 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
-public interface DimmableLightRepository extends DeviceRepository {}
+public interface DimmableLightRepository extends SwitchableRepository {}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Dimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Dimmer.java
index 2658b35..af00025 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Dimmer.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Dimmer.java
@@ -1,8 +1,10 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
+import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
+import javax.persistence.OneToMany;
/** Represents a generic dimmer input device */
@Entity
@@ -11,4 +13,17 @@ public abstract class Dimmer extends InputDevice {
public Dimmer(String kind) {
super(kind);
}
+
+ @OneToMany(mappedBy = "dimmer")
+ private Set lights;
+
+ /**
+ * Get the lights connected to this dimmer
+ *
+ * @return duh
+ */
+ @Override
+ public Set getOutputs() {
+ return this.lights;
+ }
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputDevice.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputDevice.java
index e632178..da45b67 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputDevice.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputDevice.java
@@ -1,14 +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();
+ }
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java
index 0a90998..ce3745c 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java
@@ -1,8 +1,6 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
-import java.util.Set;
import javax.persistence.Entity;
-import javax.persistence.OneToMany;
/**
* Represents a dimmer able to set absolute intensity values (i.e. knowing the absolute intensity
@@ -10,62 +8,19 @@ import javax.persistence.OneToMany;
*/
@Entity
public class KnobDimmer extends Dimmer {
+
public KnobDimmer() {
super("knob-dimmer");
}
- @OneToMany(mappedBy = "dimmer")
- private Set lights;
-
/**
- * Increases or decreases the current intensity level by 5, moving between absolute multiples of
- * 5 between 0 and 100, of all dimmable lights mapped to this knob
+ * Sets absolutely the intensity level of all lights connected
*
- * @param inc The direction the knob is turned with
+ * @param intensity the intensity (must be from 0 to 100)
*/
- public void modifyIntensity(boolean inc) {
-
- for (DimmableLight dl : lights) {
- int remainder = dl.getIntensity() / 5;
-
- if (inc) {
- dl.setIntensity(dl.getIntensity() - remainder);
- dl.setIntensity((dl.getIntensity() + 5) % 105);
- } else {
- dl.setIntensity(dl.getIntensity() + (5 - remainder));
- dl.setIntensity((dl.getIntensity() - 5) % 105);
- }
+ public void setLightIntensity(int intensity) {
+ for (DimmableLight dl : getOutputs()) {
+ dl.setIntensity(intensity);
}
}
-
- /**
- * Adds a DimmableLight to this set of DimmableLights
- *
- * @param dl The DimmableLight to be added
- */
- public void addLight(DimmableLight dl) {
- lights.add(dl);
- }
-
- /**
- * Removes the given DimmableLight
- *
- * @param dl The DimmableLight to be removed
- */
- public void removeLight(DimmableLight dl) {
- lights.remove(dl);
- }
-
- /** Clears this set */
- public void clearSet() {
- lights.clear();
- }
-
- public void setLights(Set lights) {
- this.lights = lights;
- }
-
- public Set getLights() {
- return lights;
- }
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Light.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Light.java
deleted file mode 100644
index 68a819b..0000000
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Light.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
-
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.Inheritance;
-import javax.persistence.InheritanceType;
-import javax.validation.constraints.NotNull;
-
-/** Represents a generic light */
-@Entity
-@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
-public abstract class Light extends OutputDevice {
-
- /** Whether the light is on or not */
- @Column(name = "light_on", nullable = false)
- @NotNull
- boolean on;
-
- protected Light(String kind) {
- super(kind);
- this.on = false;
- }
-
- public boolean isOn() {
- return on;
- }
-
- public void setOn(boolean on) {
- this.on = on;
- }
-}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/OutputDevice.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/OutputDevice.java
index 39e5dd0..c5b401f 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/OutputDevice.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/OutputDevice.java
@@ -1,8 +1,6 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
-import javax.persistence.Entity;
-import javax.persistence.Inheritance;
-import javax.persistence.InheritanceType;
+import javax.persistence.*;
/**
* Represents a generic output device, i.e. something that causes some behaviour (light, smartPlugs,
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLight.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLight.java
index 8f07377..de9c10a 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLight.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLight.java
@@ -1,11 +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 Light {
+public class RegularLight extends Switchable {
+
+ /** Whether the light is on or not */
+ @Column(name = "light_on", nullable = false)
+ @NotNull
+ boolean on;
+
public RegularLight() {
super("regular-light");
+ this.on = false;
+ }
+
+ @Override
+ public boolean isOn() {
+ return on;
+ }
+
+ @Override
+ public void setOn(boolean on) {
+ this.on = on;
}
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLightRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLightRepository.java
index 43c4e17..cad8831 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLightRepository.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLightRepository.java
@@ -1,3 +1,3 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
-public interface RegularLightRepository extends DeviceRepository {}
+public interface RegularLightRepository extends SwitchableRepository {}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java
index e15805b..a30e4f8 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java
@@ -1,5 +1,7 @@
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;
@@ -8,25 +10,129 @@ import javax.validation.constraints.NotNull;
@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)
+ @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;
+
/**
- * Icon and image are to be given as byte[]. In order to get an encoded string from it, the
+ * 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
- @Column(name = "icon", columnDefinition = "TEXT")
- private byte[] icon;
-
@Lob
@Column(name = "image", columnDefinition = "TEXT")
- private byte[] image;
+ private String image;
+
+ @ManyToOne
+ @JoinColumn(name = "user_id", updatable = false, insertable = false)
+ @GsonExclude
+ private User user;
/**
* User that owns the house this room is in as a foreign key id. To use when updating and
@@ -65,19 +171,19 @@ public class Room {
this.name = name;
}
- public byte[] getIcon() {
+ public Icon getIcon() {
return icon;
}
- public void setIcon(byte[] icon) {
+ public void setIcon(Icon icon) {
this.icon = icon;
}
- public byte[] getImage() {
+ public String getImage() {
return image;
}
- public void setImage(byte[] image) {
+ public void setImage(String image) {
this.image = image;
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java
index e352a52..525ceb3 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java
@@ -1,6 +1,8 @@
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;
@@ -11,6 +13,12 @@ import javax.validation.constraints.NotNull;
@Entity
public class Sensor extends InputDevice {
+ public static final Map 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 */
@@ -27,8 +35,8 @@ public class Sensor extends InputDevice {
}
/** The value of this sensor according to its sensor type */
- @Column(nullable = false)
- private int value;
+ @Column(nullable = false, length = 10, precision = 1)
+ private BigDecimal value;
/** The type of this sensor */
@Column(nullable = false)
@@ -44,15 +52,20 @@ public class Sensor extends InputDevice {
this.sensor = sensor;
}
- public int getValue() {
+ public BigDecimal getValue() {
return this.value;
}
- public void setValue(int newValue) {
+ public void setValue(BigDecimal newValue) {
this.value = newValue;
}
public Sensor() {
super("sensor");
}
+
+ @Override
+ public String toString() {
+ return "Sensor{" + "value=" + value + ", sensor=" + sensor + '}';
+ }
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java
index e0d7981..fe936b3 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java
@@ -6,17 +6,19 @@ import javax.validation.constraints.NotNull;
/** A smart plug that can be turned either on or off */
@Entity
-public class SmartPlug extends OutputDevice {
+public class SmartPlug extends Switchable {
/** Whether the smart plug is on */
@Column(name = "smart_plug_on", nullable = false)
@NotNull
private boolean on;
+ @Override
public boolean isOn() {
return on;
}
+ @Override
public void setOn(boolean on) {
this.on = on;
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlugRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlugRepository.java
index 0b2fd344..08d145d 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlugRepository.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlugRepository.java
@@ -1,3 +1,3 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
-public interface SmartPlugRepository extends DeviceRepository {}
+public interface SmartPlugRepository extends SwitchableRepository {}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switch.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switch.java
index 2576d38..d819dfe 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switch.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switch.java
@@ -1,12 +1,18 @@
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;
/** A switch input device */
@Entity
public class Switch extends InputDevice {
+ @OneToMany(mappedBy = "switchDevice")
+ private Set switchables = new HashSet<>();
+
/** The state of this switch */
@Column(nullable = false, name = "switch_on")
private boolean on;
@@ -22,6 +28,15 @@ public class Switch extends InputDevice {
*/
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());
}
/**
@@ -32,4 +47,8 @@ public class Switch extends InputDevice {
public boolean isOn() {
return on;
}
+
+ public Set getOutputs() {
+ return switchables;
+ }
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switchable.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switchable.java
new file mode 100644
index 0000000..5ba0702
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switchable.java
@@ -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_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;
+ }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SwitchableRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SwitchableRepository.java
new file mode 100644
index 0000000..589542d
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SwitchableRepository.java
@@ -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 extends DeviceRepository {}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java
index ba86712..dc6766d 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java
@@ -1,6 +1,7 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import io.swagger.annotations.ApiModelProperty;
+import java.util.Objects;
import javax.persistence.*;
/** A user of the Smarthut application */
@@ -9,7 +10,7 @@ public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
- @Column(name = "id", updatable = false, nullable = false)
+ @Column(name = "id", updatable = false, nullable = false, unique = true)
@ApiModelProperty(hidden = true)
private Long id;
@@ -17,8 +18,8 @@ public class User {
@Column(nullable = false)
private String name;
- /** The full name of the user */
- @Column(nullable = false)
+ /** The full username of the user */
+ @Column(nullable = false, unique = true)
private String username;
/** A properly salted way to store the password */
@@ -105,4 +106,22 @@ public class User {
+ 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);
+ }
}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/SensorUpdateTasks.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/SensorUpdateTasks.java
new file mode 100644
index 0000000..b1614aa
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/SensorUpdateTasks.java
@@ -0,0 +1,62 @@
+package ch.usi.inf.sa4.sanmarinoes.smarthut.scheduled;
+
+import ch.usi.inf.sa4.sanmarinoes.smarthut.controller.MotionSensorController;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.controller.SensorController;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.MotionSensorRepository;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Sensor;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.SensorRepository;
+import java.math.BigDecimal;
+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) updates as required by milestone one */
+@Component
+public class SensorUpdateTasks {
+
+ @Autowired private SensorRepository sensorRepository;
+
+ @Autowired private MotionSensorRepository motionSensorRepository;
+
+ @Autowired private SensorController sensorController;
+
+ @Autowired private MotionSensorController motionSensorController;
+
+ /** 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));
+ });
+ }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/AuthenticationMessageListener.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/AuthenticationMessageListener.java
new file mode 100644
index 0000000..89415aa
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/AuthenticationMessageListener.java
@@ -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 newHandler(
+ final Session session, BiConsumer 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();
+ }
+ }
+ };
+ }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java
new file mode 100644
index 0000000..503667a
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java
@@ -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 getEndpointInstance(Class 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;
+ }
+ }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java
new file mode 100644
index 0000000..bc2f90e
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java
@@ -0,0 +1,85 @@
+package ch.usi.inf.sa4.sanmarinoes.smarthut.socket;
+
+import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.didThrow;
+
+import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonConfig;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+import com.google.gson.Gson;
+import java.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 unauthorizedClients = Collections.synchronizedSet(new HashSet<>());
+
+ private Multimap 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 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 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 long broadcast(Object message, User u) {
+ final Collection sessions = authorizedClients.get(u);
+ return sessions.stream()
+ .parallel()
+ .filter(didThrow(s -> s.getBasicRemote().sendText(gson.toJson(message))))
+ .count();
+ }
+
+ /**
+ * 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);
+ }));
+ }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java
index d9fcf12..99d363b 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java
@@ -1,14 +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 class Utils {
+public final class Utils {
private Utils() {}
+ @FunctionalInterface
+ public interface ConsumerWithException {
+ void apply(T input) throws Throwable;
+ }
+
public static List toList(Iterable iterable) {
return StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList());
}
+
+ public static Predicate didThrow(ConsumerWithException consumer) {
+ return (t) -> {
+ try {
+ consumer.apply(t);
+ return true;
+ } catch (Throwable e) {
+ System.err.println(e.getMessage());
+ return false;
+ }
+ };
+ }
}
diff --git a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/AuthenticationTests.java b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/AuthenticationTests.java
index 60761cd..d13104f 100644
--- a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/AuthenticationTests.java
+++ b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/AuthenticationTests.java
@@ -8,6 +8,8 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateRegistrationException;
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UnauthorizedException;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import java.util.Map;
@@ -25,6 +27,10 @@ public class AuthenticationTests extends SmartHutTest {
@Autowired private TestRestTemplate restTemplate;
+ @Autowired private UserRepository userRepository;
+
+ @Autowired private ConfirmationTokenRepository tokenRepository;
+
private UserRegistrationRequest getDisabledUser() {
final UserRegistrationRequest disabledUser = new UserRegistrationRequest();
disabledUser.setName("Disabled User");
@@ -34,15 +40,6 @@ public class AuthenticationTests extends SmartHutTest {
return disabledUser;
}
- private static final UserRegistrationRequest enabledUser = new UserRegistrationRequest();
-
- static {
- enabledUser.setName("Enabled User");
- enabledUser.setEmail("enabled@example.com");
- enabledUser.setUsername("enabled");
- enabledUser.setPassword("password");
- }
-
@Override
protected void setUp() {
final ResponseEntity res =
@@ -50,12 +47,7 @@ public class AuthenticationTests extends SmartHutTest {
this.url("/register"), getDisabledUser(), OkResponse.class);
assertThat(res.getStatusCode().equals(HttpStatus.OK));
- final ResponseEntity res2 =
- this.restTemplate.postForEntity(
- this.url("/register"), enabledUser, OkResponse.class);
- assertThat(res2.getStatusCode().equals(HttpStatus.OK));
-
- // TODO: email confirmation for enabledUser
+ registerTestUser(restTemplate, userRepository, tokenRepository);
}
@Test
@@ -230,4 +222,18 @@ public class AuthenticationTests extends SmartHutTest {
assertThat(res.getBody() != null);
assertThat(res.getBody().isUserDisabled());
}
+
+ @Test
+ public void loginShouldReturnTokenWithEnabledUser() {
+ final JWTRequest request = new JWTRequest();
+ request.setUsernameOrEmail("enabled");
+ request.setPassword("password");
+
+ final ResponseEntity res =
+ this.restTemplate.postForEntity(url("/auth/login"), request, JWTResponse.class);
+ assertThat(res.getStatusCode().equals(HttpStatus.OK));
+ assertThat(res.getBody() != null);
+ assertThat(res.getBody().getToken() != null);
+ assertThat(!res.getBody().getToken().isEmpty());
+ }
}
diff --git a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmartHutTest.java b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmartHutTest.java
index 5c6e097..f2b737a 100644
--- a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmartHutTest.java
+++ b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmartHutTest.java
@@ -1,6 +1,18 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationToken;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
import org.junit.jupiter.api.BeforeEach;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.reactive.function.client.WebClient;
public abstract class SmartHutTest {
private boolean setupDone = false;
@@ -15,6 +27,38 @@ public abstract class SmartHutTest {
protected void setUp() {}
+ protected static final UserRegistrationRequest enabledUser = new UserRegistrationRequest();
+
+ static {
+ enabledUser.setName("Enabled User");
+ enabledUser.setEmail("enabled@example.com");
+ enabledUser.setUsername("enabled");
+ enabledUser.setPassword("password");
+ }
+
+ protected void registerTestUser(
+ final TestRestTemplate restTemplate,
+ final UserRepository userRepository,
+ final ConfirmationTokenRepository tokenRepository) {
+ final ResponseEntity res2 =
+ restTemplate.postForEntity(this.url("/register"), enabledUser, OkResponse.class);
+ assertThat(res2.getStatusCode().equals(HttpStatus.OK));
+
+ final User persistedEnabledUser = userRepository.findByUsername("enabled");
+ final ConfirmationToken token = tokenRepository.findByUser(persistedEnabledUser);
+
+ final ResponseEntity res3 =
+ WebClient.create(getBaseURL())
+ .get()
+ .uri("/register/confirm-account?token=" + token.getConfirmationToken())
+ .retrieve()
+ .toBodilessEntity()
+ .block();
+
+ assertThat(res3.getStatusCode().is2xxSuccessful());
+ assertThat(userRepository.findByUsername("enabled").getEnabled());
+ }
+
@BeforeEach
void setUpHack() {
if (!setupDone) {
diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties
index ce6fe39..673d02a 100644
--- a/src/test/resources/application.properties
+++ b/src/test/resources/application.properties
@@ -28,7 +28,7 @@ server.port = 2000
email.registrationSubject=Complete your SmartHut.sm registration
email.registration=To confirm your registration, please click here:
-email.registrationPath=http://localhost:8080/register/confirm-account?token=
+email.registrationPath=http://localhost:2000/register/confirm-account?token=
email.resetpasswordSubject=SmartHut.sm password reset
email.resetpassword=To reset your password, please click here: